allasm.ru

    Меню

 

19.1 Частичные задержки регистра

   Частичная задержка регистра - это проблема, которая возникает, когда вы пишите в часть 32-х битного регистра, а затем читаете из всего регистра или его большей части. Пример:

        MOV AL, BYTE PTR [M8]
        MOV EBX, EAX            ; частичная задержка регистра

   Происходит задержка в 5-6 тактов. Причина состоит в том, что в соответствие AL был поставлен временный регистр (чтобы сделать его независимым от AH). Модулю выполнения пришлось ждать, пока запись в AL не будет выведена из обращения, прежде чем станет возможным соединить значение AL c тем, что находится в остальной части EAX. Задержку можно избежать, изменив код, поменяв код на:

        MOVZX EBX, BYTE PTR [MEM8]
        AND EAX, 0FFFFFF00h
        OR EBX, EAX

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

   Вам нужно остерегаться частичных задержек, когда вы смешиваете различные размеры данных (8, 16 и 32):

        MOV BH, 0
        ADD BX, AX              ; задержка
        INC EBX                 ; задержка

   У вас не будет задержки при чтении части регистра после записи в целый регистр или его большую часть:

        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]'

        XOR EAX, EAX
        MOV AL, BYTE PTR [M8]

   Процессоры PPro, PII и PIII делают специальное исключение для этой комбинации, поэтому при последующем чтении из EAX задержки не возникнет. Происходит это потому, что регистр помечается как пустой, когда он XOR'ится сам с собой. Процессор помнит, что верхние 24 бита равны нулю и за счет этого избегается задержка. Этот механизм работает только со следующими комбинациями:

        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 снаружи цикла:

        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'ить полный регистр:

        ADD BL, AL
        MOV [MEM8], BL
        XOR EBX, EBX            ; нейтрализируем BL
        CALL _HighLevelFunction

   Большинство языков высокого уровня PUSH'ат EBX в начале процедуры, что в вышеприведенном примере привело к частичной задержке регистра, если бы ее не нейтрализовали.

   Обнуление регистра с помощью XOR не устраняет его зависимость от предыдущих инструкций:

        DIV EBX
        MOV [MEM], EAX

MOV EAX, 0 ; прерываем зависимость XOR EAX, EAX ; предотвращаем частичную задержку регистра MOV AL, CL ADD EBX, EAX

   Обнуление регистра дважды может показаться излишней, но без 'MOV EAX, 0' последние инструкции будут ждать, пока выполниться медленный DIV, а без 'XOR EAX, EAX' случиться частичная задержка регистра.

   Инструкция 'FNSTSW AX' особенная: в 32-х битном режиме она ведет себя так же, как если бы писала в весь EAX. Фактически она делает в 32-х битном режиме следующее:

AND EAX,0FFFF0000h / FNSTSW TEMP / OR EAX,TEMP

   Поэтому при чтении регистра после этой инструкции у вас не возникнет частичной задержки регистра в 32-х битном режиме:

    FNSTSW AX / MOV EBX,EAX         ; задержка только в 16-ти битном режиме
    MOV AX,0  / FNSTSW AX           ; задержка только в 32-х битном режиме

19.2 Частичные задержки флагов

Регистр флагов также может вызвать частичную задержку:

        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), частичная задержка регистра может случиться, если были изменены какие-то биты регистра флагов, а затем считаны неизмененные.

        CMP EAX, EBX
        INC ECX
        JC  XX          ; задержка

но не при чтении только изменных битов:

        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) для сохранения значения флага, чтобы избежать задержки.

Примеры:

    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, т.н. короткая форма) может возникнуть задержка, похожая на частичную задержку флагов:

    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 Частичные задержки памяти

   Частичная задержка памяти похожа на частичную задержку регистра. Она случается, когда вы смешиваете размеры данных применительно к одному адресу в памяти:

        MOV BYTE PTR [ESI], AL
        MOV EBX, DWORD PTR [ESI]        ; частичная задержка в памяти

   Здесь случается задержка, потому что процессор должен скомбинировать байт, записанный из AL с тремя следующими байтами, которые были в памяти раньше, чтобы получить все четыре байта, необходимые для произведения записи в EBX. Потери приблизительно равны 7-8 тактов.

   В отличии от частичных задержек регистра, частичная задержка памяти случается, когда вы записывает операнд в память, а затем читаете его часть, если она не начинается по тому же адресу:

        MOV DWORD PTR [ESI], EAX
        MOV BL, BYTE PTR [ESI]          ; нет задержки
        MOV BH, BYTE PTR [ESI+1]        ; задержка

   Вы можете избежать этой задержки, если измените последнюю лини на 'MOV BH, AH', но подобное решение невозможно в ситуации, подобной этой:

        FISTP QWORD PTR [EDI]
        MOV EAX, DWORD PTR [EDI]
        MOV EDX, DWORD PTR [EDI+4]      ; задержка

   Интересно, что частичная задержка памяти может также случиться при записи и чтении совершенно разны адресов, если у них одинаковое set-значение в разных банках кэша:

        MOV BYTE PTR [ESI], AL
        MOV EBX, DWORD PTR [ESI+4092]   ; нет задержки
        MOV ECX, DWORD PTR [ESI+4096]   ; задержка