Д о п о л н е н и е
Раздел Сокровищница | , дата публикации 04 апреля 2001г. |
В наше время крупных проектов на Васике и Яве, фантастического снижения цен на мегагерцы и мегабайты, скриптуемых языков, COM и супер технологии NET на вагончике сидюков, писать критичные по времени процедуры на ассемблере не модно, можно получить жалостливую усмешку. Все многозначительно обсуждают что <вот у си оптимизация>, а ассемблер это так не переносимо - вдруг Intel загнется ;-).
Где-то два года назад, я разрабатывал программу сервер для интенсивной круглосуточной работы, в том числе работы по забору и переформатированию почты. Одним из этапов форматирования являлось преобразования из koi8,iso,dos,mac в любимый win1251.
Поскольку это часто выполняемая для больших объемов данных операция, то был смысл её оптимизировать. Сразу отбрасывались вложенные циклы, кучи условий вида if (Ch>X) and (Ch<Y) then : в цикле for i:=1 to Length(S), и выбор падал на таблицы перекодировки. Я утешил себя тем, что при преобразования из одной кодировки в другу перемещаются не 66, а почти 128 символов (в худшем случае все 255). Табличка koi8-dos стандартна, её можно легко найти в интернете. Кроме того, она обладает редким достоинством - она взаимооднозначна (каждому символу в одной кодировке соответствует один и только один в другой). Таблички dos-win1251, win1251-dos сам бог велел брать у M$ (функции OemToChar, CharToOem), остальные я просто стянул из FAR'a (да простит меня Е. Рошал ;-). После этого я написал тривиальные процедуры перекодировки на ObjPas, посмотрел на получаемый в результате машинный код и переписал всё на асме (я не сторонник большого количества асма в программе, это показатель неправильно выбранного алгоритма, но в таких случаях он необходим). Через год, увидев обсуждение этого вопроса в КД, я с интересом стал ждать итоговый вариант чтобы стянуть его и приладить в своей программе. Уж год прошел, а такого всё нет
Поэтому шлю свой вариант (это не чудо оптимизации, но всё же лучше того что было в оригинале). TmcCodePageCharsetTable = array [Byte] of Byte; PmcCodePageCharsetTable =^TmcCodePageCharsetTable; // Из таблиц перекодировки A->B, B->C создать A->C // if SafeASCII then в позициях 0..127 будут байты 0..127 procedure mcCodePageCharsetGen (pS1,pS2,pDst: PmcCodePageCharsetTable; SafeASCII: Boolean); Asm//eax-pS1, edx-pS2, ecx-pDst push EBX push ESI push EDI mov ESI,EAX //pS1 mov EBX,EDX //pS2 mov EDI,ECX //pDst mov EDX,ECX //pDst - save xor ecx,ecx //index xor eax,eax @@R: lodsb //A[i] xlat //B[A[i]] stosb //C[i]:=B[A[i]] inc ecx test cl,cl jnz @@R //SafeAscii (0..127) cmp SafeASCII,cl //0 je @@q //FALSE xor ecx,ecx mov edi,edx //pDst @@Fill: mov al,cl stosb inc ecx cmp cl,$80 jb @@Fill @@q: pop EDI pop ESI pop EBX End;// {var i: LongInt; Begin FillChar(pDst^,SizeOf(TmcCodePageCharsetTable),0); for i:=0 to 255 do pDst^[i]:=pS2^[pS1^[i]]; End;//mcCodePageCharsetJoin} //Создать обратную таблицу перекодировки procedure mcCodePageCharsetGen (pSrc,pDst: PmcCodePageCharsetTable; SafeASCII: Boolean); const xBound = 32;//эвристический порог { Несколько слов об xBound: Поскольку в общем случае (пример dos<->win) одному символу одной таблицы может соотвествовать несколько символов другой, надо выбирать какой из них считать правильным. Я решил считать им первый помещаемый символ >=xBound, а само значение xBound выбрать = ' ' (поскольку все символы меньше пробела M$ не любит и вряд ли будет рассовывать по всей табличке) } Asm//eax-pSrc, edx-pDst, cl-1/0-boolean push EBX push ESI push EDI push ECX //push SafeASCII mov ESI,EAX //pSrc //Clear Dst Table xor eax,eax xor ecx,ecx //index mov cl,$40 mov EDI,EDX //pDst rep stosd //Create Reverse @@R: lodsb //A[i] lea ebx, [edx+eax] cmp byte ptr [ebx],xBound jae @@Already mov [ebx], cl //B[A[i]]:=i @@Already: inc ecx test cl,cl jnz @@R //SafeAscii (0..127) pop ECX //pop SafeASCII test cl,cl jz @@q //FALSE xor ecx,ecx mov edi,edx //pDst @@Fill: mov al,cl stosb inc ecx cmp cl,$80 jb @@Fill @@q: pop EDI pop ESI pop EBX End;// // по табличке преобразования pCPCT преобразовать данные из pSrc и записать их в pDst procedure mcCodePageCharsetConvert (pSrc,pDst: Pointer; DataLen: LongInt; pCPCT: PmcCodePageCharsetTable); Asm//eax-pSrc, edx-pDst, ecx-DataLen push ESI push EDI push EBX test ecx,ecx //DataLen jz @@q //=0 mov esi,eax test edx,edx jnz @@pDstAssigned mov edx,eax //pDst:=pSrc @@pDstAssigned: mov edi,edx //pDst mov ebx,pCPCT test ebx,ebx jnz @@pCPCTAssigned call Move //eax,edx,ecx jmp @@q @@pCPCTAssigned: xor eax,eax //??? @@R: lodsb xlat stosb dec ecx jnz @@R @@q: pop EBX pop EDI pop ESI End;//mcCodePageCharsetConvert
Специально для