"Оптимизация для PENTIUM процессора" - читать интересную книгу автора

MOV EBX,[EAX] ; читаем первые 4 байта
ADD EAX,4 ; увеличиваем указатель
L1: LEA ECX,[EBX-01010101H] ; уменьшаем на 1 каждый байт
XOR EBX,-1 ; инвертируем все байты
AND ECX,EBX ; и эти два
MOV EBX,[EAX] ; читаем следующие 4 байта
ADD EAX,4 ; увеличиваем указатель
AND ECX,80808080H ; проверяем все битовые знаки
JZ L1 ; нет нулевых байт, продолжаем цикл
TEST ECX,00008080H ; тестируем первые два байта
JNZ SHORT L2
SHR ECX,16 ; не первые два байта
ADD EAX,2
L2: SHL CL,1 ; флаг переноса, что бы неветвить
POP EBX
SBB EAX,EDX ; считаем длину
RET ; (или RET 4 для паскаля)
STRLEN ENDP

Снова мы использовали метод перекрывания конца одной операции с началом
другого для улучшения спаривания. Я не развернул цикл, потому что это отняло
бы много времени. Строка, конечно, должна быть выравнена на 4. Всегда будут
читаться несколько байт в конце строки, так что строка не должна находиться в
конце сегмента.

Тело цикла имеет нечетное количество инструкций, так что одна всегда будет
неспарена. Используя цикл с одной неспаренной инструкции ветвления, мы делаем
упор на другой, что и сохраняет нам 1 такт.

Инструкция TEST ECX, 00008080H - не спариваемая. Вы могли бы использовать
здесь спариваемую инструкцию OR CH, CL, но вам придется вставить NOP или
что-то подобное по следующей причине: Ветвь цикла (JZ L1) обычно непредсказана,
в последний раз, когда цикл завершается. А исполнение ветвления (JNZ L2) в
первом такте цикла, после непредсказанного ветвления приведет к задержке в
5-10 тактов. Другая проблема с OR CH, CL - это то, что такая инструкция
вызовет замедление на процессорах i486 или PentiumPro, так что я решил
оставить неспариваемую инструкцию TEST.

Обработка 4 байт одновременно может быть весьма трудной. Код использует
алгоритм генерации ненулевого значения для байта, если только значение байта -
ноль. Это дает возможность тестировать все четыре байта одной операцией.
Алгоритм включает вычитание 1 из всех байт (в инструкции LEA). Я не маскировал
верхние биты каждого байта перед вычитанием, как я делал в предыдущем примере,
потому что это может привести к заему в следующем байте, но только в том
случае, если значение текущего байта - ноль, но это нас уже не волнует, т.к.
мы ищем до первого нуля. Если мы искали в обратном направлении, то после
обнаружении нуля надо перечитать двойное слово, затем протестировать все
четыре байта, что бы найти последний ноль и использовать BSWAP, что бы
изменить порядок следования байт.