Оптимизация для процессоров семейства Pentium: 19. Частичные задержки (PPro, PII и PIII) — Архив WASM.RU
19.1 Частичные задержки регистра
Частичная задержка регистра - это проблема, которая возникает, когда вы пишите в часть 32-х битного регистра, а затем читаете из всего регистра или его большей части. Пример:
Код (Text):
MOV AL, BYTE PTR [M8] MOV EBX, EAX ; частичная задержка регистраПроисходит задержка в 5-6 тактов. Причина состоит в том, что в соответствие AL был поставлен временный регистр (чтобы сделать его независимым от AH). Модулю выполнения пришлось ждать, пока запись в AL не будет выведена из обращения, прежде чем станет возможным соединить значение AL c тем, что находится в остальной части EAX. Задержку можно избежать, изменив код, поменяв код на:
Код (Text):
MOVZX EBX, BYTE PTR [MEM8] AND EAX, 0FFFFFF00h OR EBX, EAXКонечно вы можете избежать частичную задержку, поместив другие инструкции после записи, чтобы у последней было время на вывод из обращения до того, как вы начнете читать из полного регистра.
Вам нужно остерегаться частичных задержек, когда вы смешиваете различные размеры данных (8, 16 и 32):
Код (Text):
MOV BH, 0 ADD BX, AX ; задержка INC EBX ; задержкаУ вас не будет задержки при чтении части регистра после записи в целый регистр или его большую часть:
Код (Text):
MOV EAX, [MEM32] ADD BL, AL ; нет задержки ADD BH, AH ; нет задержки MOV CX, AX ; нет задержки MOV DX, BX ; задержкаСамый легкий путь избегать частиные задержки регистра - это всегда использовать полные регистры и использовать MOVZX или MOVSX при чтении из операндов более мелкого размера. Эти инструкции быстры на PPro, PII и PIII, но медленны на более ранних процессорах. Если вы хотите, чтобы ваш код выполнялся достаточно быстро на всех процессорах, то существует разумный компромисс. 'MOVZX EAX,BYTE PTR [M8]'
Код (Text):
XOR EAX, EAX MOV AL, BYTE PTR [M8]Процессоры PPro, PII и PIII делают специальное исключение для этой комбинации, поэтому при последующем чтении из EAX задержки не возникнет. Происходит это потому, что регистр помечается как пустой, когда он XOR'ится сам с собой. Процессор помнит, что верхние 24 бита равны нулю и за счет этого избегается задержка. Этот механизм работает только со следующими комбинациями:
Код (Text):
XOR EAX, EAX MOV AL, 3 MOV EBX, EAX ; нет задержки XOR AH, AH MOV AL, 3 MOV BX, AX ; нет задержки XOR EAX, EAX MOV AH, 3 MOV EBX, EAX ; задержка SUB EBX, EBX MOV BL, DL MOV ECX, EBX ; нет задержки MOV EBX, 0 MOV BL, DL MOV ECX, EBX ; задержка MOV BL, DL XOR EBX, EBX ; нет задержкиУстановка регистра в ноль вычитанием его из самого себя работает так же как XOR, но обнуление регистра с помощью инструкции MOV не предотвращает задержку.
Вы можете установите XOR снаружи цикла:
Код (Text):
XOR EAX, EAX MOV ECX, 100 LL: MOV AL, [ESI] MOV [EDI], EAX ; задержка INC ESI ADD EDI, 4 DEC ECX JNZ LLПроцессор помнит, что верхние 24 бита EAX равны нулю пока не происходит вызов прерывания, неправильного предсказания перехода или другого синхронизирующего события.
Вы должны помнить, что необходимо нейтрализовывать возможные частичные задержки регистра вышеописанным способом при вызове процедуры, которая будет PUSH'ить полный регистр:
Код (Text):
ADD BL, AL MOV [MEM8], BL XOR EBX, EBX ; нейтрализируем BL CALL _HighLevelFunctionБольшинство языков высокого уровня PUSH'ат EBX в начале процедуры, что в вышеприведенном примере привело к частичной задержке регистра, если бы ее не нейтрализовали.
Обнуление регистра с помощью XOR не устраняет его зависимость от предыдущих инструкций:
Код (Text):
DIV EBX MOV [MEM], EAX <p> MOV EAX, 0 ; прерываем зависимость XOR EAX, EAX ; предотвращаем частичную задержку регистра MOV AL, CL ADD EBX, EAXОбнуление регистра дважды может показаться излишней, но без 'MOV EAX, 0' последние инструкции будут ждать, пока выполниться медленный DIV, а без 'XOR EAX, EAX' случиться частичная задержка регистра.
Инструкция 'FNSTSW AX' особенная: в 32-х битном режиме она ведет себя так же, как если бы писала в весь EAX. Фактически она делает в 32-х битном режиме следующее:
Код (Text):
AND EAX,0FFFF0000h / FNSTSW TEMP / OR EAX,TEMPПоэтому при чтении регистра после этой инструкции у вас не возникнет частичной задержки регистра в 32-х битном режиме:
Код (Text):
FNSTSW AX / MOV EBX,EAX ; задержка только в 16-ти битном режиме MOV AX,0 / FNSTSW AX ; задержка только в 32-х битном режиме19.2 Частичные задержки флагов
Регистр флагов также может вызвать частичную задержку:
Код (Text):
CMP EAX, EBX INC ECX JBE XX ; задержкаИнструкция JBE читает и флаг переноса, и флаг нуля. Так как инструкция INC изменяет флаг нуля, но не флаг переноса, то инструкции JBE приходится подождать, пока две предыдущие инструкции не будут выведены из обращения, прежде чем она сможет скомбинировать флаг переноса от инструкции CMP с флагом нуля от инструкции INC. Подобная ситуация больше похожа на баг, чем на преднамеренную комбинацию флагов. Чтобы скорректировать эту ситуацию, измените INC ECX на ADD ECX, 1. Похожий баг, вызывающий задержку, - это 'SAHF / JL XX'/. Инструкция JL тестирует флаг знака и флаг переполнения, но не меняет последний. Чтобы исправить это, измените 'JL XX' на 'JS XX'.
Неожиданно (и в противоположность тому, что говорят руководства от Intel), частичная задержка регистра может случиться, если были изменены какие-то биты регистра флагов, а затем считаны неизмененные.
Код (Text):
CMP EAX, EBX INC ECX JC XX ; задержкано не при чтении только изменных битов:
Код (Text):
CMP EAX, EBX INC ECX JE XX ; нет задержкиЧастичные задержки флагов возникают, как правило, при использовании инструкций, которые считывают много или все биты регистра флагов, например LAHF, PUSHF, PUSHFD. Инструкции, которые вызывают задержку, если за ними идут LAHF или PUSHF(D), следующие: INC, DEC, TEST, битовые тесты, битовые сканирования, CLC, STC, CMC, CLD, STD, CLI, STI, MUL, IMUL, и все виды битовых сдвигов и вращений. Следующие инструкции не вызывают задержки: AND, OR, XOR, ADD, ADC, SUB, SBB, CMP, NEG. Странно, что TEST и AND ведут себя различно, хотя по описанию они делают с флагами одно и то же. Вы можете использовать инструкции SETcc вместо LAHF или PUSHF(D) для сохранения значения флага, чтобы избежать задержки.
Примеры:
Код (Text):
INC EAX / PUSHFD ; задержка ADD EAX,1 / PUSHFD ; нет задержки SHR EAX,1 / PUSHFD ; задержка SHR EAX,1 / OR EAX,EAX / PUSHFD ; нет задержки TEST EBX,EBX / LAHF ; задержка AND EBX,EBX / LAHF ; нет задержки TEST EBX,EBX / SETZ AL ; нет задержки CLC / SETZ AL ; задержка CLD / SETZ AL ; нет задержкиПотери при частичной задержки флагов примерно равны 4 тактам.
19.3 Задержки флагов после сдвигов и вращений
При чтении любого флагового бита после сдвига или вращения (кроме сдвига и вращения на 1, т.н. короткая форма) может возникнуть задержка, похожая на частичную задержку флагов:
Код (Text):
SHR EAX,1 / JZ XX ; нет задержки SHR EAX,2 / JZ XX ; задержка SHR EAX,2 / OR EAX,EAX / JZ XX ; нет задержки SHR EAX,5 / JC XX ; задержка SHR EAX,4 / SHR EAX,1 / JC XX ; нет задержки SHR EAX,CL / JZ XX ; задержка, даже если CL = 1 SHRD EAX,EBX,1 / JZ XX ; задержка ROL EBX,8 / JC XX ; задержкаПотери для этого вида задержек приблизительно равны 4 тактам.
19.4 Частичные задержки памяти
Частичная задержка памяти похожа на частичную задержку регистра. Она случается, когда вы смешиваете размеры данных применительно к одному адресу в памяти:
Код (Text):
MOV BYTE PTR [ESI], AL MOV EBX, DWORD PTR [ESI] ; частичная задержка в памятиЗдесь случается задержка, потому что процессор должен скомбинировать байт, записанный из AL с тремя следующими байтами, которые были в памяти раньше, чтобы получить все четыре байта, необходимые для произведения записи в EBX. Потери приблизительно равны 7-8 тактов.
В отличии от частичных задержек регистра, частичная задержка памяти случается, когда вы записывает операнд в память, а затем читаете его часть, если она не начинается по тому же адресу:
Код (Text):
MOV DWORD PTR [ESI], EAX MOV BL, BYTE PTR [ESI] ; нет задержки MOV BH, BYTE PTR [ESI+1] ; задержкаВы можете избежать этой задержки, если измените последнюю лини на 'MOV BH, AH', но подобное решение невозможно в ситуации, подобной этой:
Код (Text):
FISTP QWORD PTR [EDI] MOV EAX, DWORD PTR [EDI] MOV EDX, DWORD PTR [EDI+4] ; задержкаИнтересно, что частичная задержка памяти может также случиться при записи и чтении совершенно разны адресов, если у них одинаковое set-значение в разных банках кэша:
© Агнер Фог, пер. AquilaКод (Text):
MOV BYTE PTR [ESI], AL MOV EBX, DWORD PTR [ESI+4092] ; нет задержки MOV ECX, DWORD PTR [ESI+4096] ; задержка
Оптимизация для процессоров семейства Pentium: 19. Частичные задержки (PPro, PII и PIII)
Дата публикации 22 авг 2002