Использование ассемблера в Дельфи


         

Используйте 32-битные алгоритмы, везде, где только возможно


Если только невозможно иначе, то вы не должны использовать инструкции, которые оперируют словами, более правильно использовать те, которые работают с переменными типа двойное слово. Байтовый доступ еще может иногда использоваться, но остерегайтесь использовать операции со словами. Ниже пример использования 32-битного алгоритма для поиска символа в строке. Идея основана на генерации уникального значения, если и только если символ найден. Поскольку пример обрабатывает строку по четыре байта за раз, то это значительно быстрее, чем обработка по одному байту за раз, несмотря на дополнительное усложнение, поскольку требуется обрабатывать сразу четыре байта.

function ScanStrForChar(C: Char; S: String): Integer; register;

asm

push EBX

push EDI

push ESI

test EDX,EDX

jz @notfound

mov ESI,[EDX-4]

test ESI,ESI

jz @notfound

add ESI,EDX

mov ah,al



mov di,ax

shl EDI,16

or di,ax

mov EAX,EDX

lea EDX,[EAX+3]

@L1:

cmp EAX,ESI

ja @notfound

mov EBX,[EAX]

xor EBX,EDI

add EAX,4

lea ECX,[EBX-$01010101]

not EBX

and ECX,EBX

and ECX,$80808080

jz @L1

mov EBX,ECX

shr EBX,16

test ECX,$8080

jnz @L2

mov ECX,EBX

@L2:

lea EBX,[EAX+2]

jnz @L3

mov EAX,EBX

@L3:

shl cl,1

sbb EAX,EDX

inc EAX

@ending:

pop ESI

pop EDI

pop EBX

ret

@notfound:

xor EAX,EAX

jmp @ending

end;

В зависимости от длины обрабатываемых строк, функция может быть быстрее стандартной функции pos раза в два. Заметим, что должна прочитать от одного до трех символов в конце строки, но в текущей версии компилятор всегда размещает строки по модулю четыре, так что, это не приносит проблем, но нельзя гарантировать, что в следующих версиях это будет так же. Вы можете устранить эту проблему, путем расчета остатка символов в конце строки по модулю четыре и обработать остаток побайтно. Вы потеряете некоторое быстродействие, но выиграете в надежности. Заметим, что компилятор добавляет одну или несколько инструкций ret после jmp @ending. Но они никогда не будут выполнены, поскольку мы включили инструкцию ret сами.

Вы должны избегать подобных вещей, поскольку если нужен фрейм стека, то вы должны будете сами написать код выхода (см. главу 1.2, где рассмотрены коды входа и выхода). В вышеприведенном примере нет фрейма стека, так что нет нужды и в коде выхода. Вы можете избежать этой проблемы, путем добавления инструкции jmp для условия, когда символ не найден, это просто пропустит установку результата в ноль, если строка пустая или символ не найден. После этого пример будет выглядеть так:

...

shl cl,1

sbb EAX,EDX

inc EAX

jmp @ending

@notfound:

xor EAX,EAX

@ending:

pop ESI

pop EDI

pop EBX

end;

Но, в этом случае вы будете вынуждены всегда добавлять дополнительную инструкцию jmp в ваш алгоритм, который немного от этого замедлится. Если вы обрабатывает достаточно длинные строки, то это почти незаметно на общем процессе обработке, и добавленный код может быть субъектом для оптимизации в дальнейшем, когда это станет важным.



Содержание раздела