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

TEST ECX, ECX
JZ SHORT L2
L1: MOV EAX, [ESI+4*EDX] ; u
NEG EAX ; u
MOV [EDI+4*EDX], EAX ; u
INC EDX ; v (спаривание)
CMP EDX, ECX ; u
JB L1 ; v (спаривание)
L2:

Используя один регистр как счетчик и как индекс мы задействуем меньше
инструкций в теле цикла, но он все еще исполняется за 4 такта, поскольку у нас
есть две неспаренные инструкции.

Счетчик, стремящийся к нулю
---------------------------
Желая избавиться от инструкции CMP, из примера 3, мы могли бы обозначить конец
цикла нулевым значением счетчика и использовать флаг нуля (ZF) для обнаружения
конца цикла, как это мы делали в примере 2. Единственный путь сделать это -
исполнить цикл наоборот, читая последние элементы первыми. Правда, кеш данных
оптимизировался для доступа к поступающим подряд данным, а не наоборот, так
что постоянные промахи кеша весьма вероятны, что бы избегать этого мы должны
запускать счетчик от -N и увеличивать до нуля. А регистр базы должен указывать
на конец массива, а не на начало:

Пример 4:

MOV ESI, [A]
MOV EAX, [N]
MOV EDI, [B]
XOR ECX, ECX
LEA ESI, [ESI+4*EAX] ; указатель на конец массива A
SUB ECX, EAX ; -N
LEA EDI, [EDI+4*EAX] ; указатель на конец массива B
JZ SHORT L2
L1: MOV EAX, [ESI+4*ECX] ; u
NEG EAX ; u
MOV [EDI+4*ECX], EAX ; u
INC ECX ; v (спаривание)
JNZ L1 ; u
L2:

Теперь у нас пять инструкций в теле цикла, но на исполнение по прежнему
тратиться 4 такта из-за плохого спаривания. (Если адреса и длины массивов
постоянны, то мы можем сохранить два регистра заменяя A+SIZE для ESI и B+SIZE
для EDI). А теперь давайте посмотрим как можно улучшить спаривание.

Спаривание расчета с управлением цикла
--------------------------------------
Мы можем захотеть ускорить цикл, спаривая инструкции его тела с инструкциями,