Небольшие ветки в современных процессорах - PullRequest
0 голосов
/ 03 марта 2019

Как современные процессоры, такие как Kaby Lake, обрабатывают небольшие ветви?(в приведенном ниже коде это переход к метке LBB1_67).Из того, что я знаю, ветвь не будет вредной, потому что переход уступает 16-байтовому размеру блока, который является размером окна декодирования.

Или возможно, что из-за некоторой макрооперации ветвьбудет полностью исключен?

        sbb     rdx, qword ptr [rbx - 8]
        setb    r8b
        setl    r9b
        mov     rdi, qword ptr [rbx]
        mov     rsi, qword ptr [rbx + 8]
        vmovdqu xmm0, xmmword ptr [rbx + 16]
        cmp     cl, 18
        je      .LBB1_67
        mov     r9d, r8d
.LBB1_67:                               #   in Loop: Header=BB1_63 Depth=1
        vpcmpeqb        xmm0, xmm0, xmmword ptr [rbx - 16]
        vpmovmskb       ecx, xmm0
        cmp     ecx, 65535
        sete    cl
        cmp     rdi, qword ptr [rbx - 32]
        sbb     rsi, qword ptr [rbx - 24]
        setb    dl
        and     dl, cl
        or      dl, r9b

1 Ответ

0 голосов
/ 03 марта 2019

Не существует особых случаев для коротких расстояний между ветвями в любых процессорах x86.Даже безусловная jmp для следующей инструкции (архитектурно nop) требует правильного предсказания ветвления, чтобы быть обработанным эффективно;если вы кладете достаточно их в ряд, у вас заканчиваются записи BTB, и производительность падает с обрыва. Медленная jmp-инструкция

Выборка / декодирование - это только незначительная проблема ;да, очень короткая ветвь в одной и той же строке кэша все еще будет попадать в L1i и, вероятно, в кэш uopНо маловероятно, что декодеры будут в особом случае предсказывать принятый скачок вперед и использовать предварительное декодирование нахождения границ инструкции из одного блока, который включает в себя как ветвь, так и цель.

Когда выполняется инструкциядекодированные в uops и переданные во внешний интерфейс, значения регистров недоступны;они доступны только в бэк-энде выполнения не по порядку.

Основная проблема заключается в том, что при выполнении инструкций после .LBB1_67: архитектурное состояние отличается в зависимости от того, была ли выбрана ветвь или нет,То же относится и к микроархитектурному состоянию (RAT = таблица распределения регистров).

Либо:

  • r9 зависит от результата sbb / setl (mov r9d, r8d не работал)
  • r9 зависит от результата sbb / setb (mov r9d, r8d выполнено)

Условные ветви называются "управляющими зависимостями"в терминологии компьютерной архитектуры.Предсказание ветвлений + спекулятивное выполнение позволяет избежать превращения управляющих зависимостей в зависимости от данных.Если прогноз je не был получен, результат setl (старое значение r9) перезаписывается на mov и больше нигде не доступен.

Невозможно восстановить данныепосле обнаружения неверного прогноза в je (на самом деле следовало принять), особенно в общем случае.Текущие процессоры x86 не пытаются искать альтернативный путь, воссоединяющийся с выбранным путем или что-либо выяснять о том, что он делает.

Если cl не был готов в течение длительного времени, то это неверный прогноздолгое время не было обнаружено, многие инструкции после or dl, r9b могли быть выполнены с использованием неправильных вводов.В общем случае единственный способ надежного и эффективного восстановления - это отказаться от всей работы, выполненной по инструкциям, с «неправильного» пути.Определить, что, например, vpcmpeqb xmm0, [rbx - 16] все еще работает, сложно, и его не искали.(Современный Intel, начиная с Sandybridge, имеет буфер порядка ветвлений (BOB), который снимает RAT на ветвях, обеспечивая эффективный откат до пропадания ветвления, как только обнаружение выполнения обнаруживает, в то же время позволяя выполнение вне очереди на ранее инструкции для продолжения во время отката. Перед этим промах ветки должен был откатиться до состояния выхода из системы.)


Некоторые процессоры для некоторых ISA не-x86 (например, PowerPC, я думаю) экспериментировали споворот вперед ветвей, которые пропускают ровно 1 инструкцию в предикацию (зависимость от данных) вместо того, чтобы спекулировать мимо них.например, Динамическое предсказание гамака для архитектур без набора команд обсуждает эту идею и даже решает, следует ли делать предикаты или нет для каждой ветви.Если ваша история предсказания ветвлений говорит, что эта ветвь предсказывает плохо, предсказание этого может быть хорошим.(Ветвь гамака - это та, которая переходит вперед по одной или нескольким инструкциям. Обнаружение точно 1 случая команды тривиально на ISA со словами команд фиксированной ширины, как RISC, но сложно на x86.)

В этом случае x86 имеет инструкцию cmovcc, операцию выбора ALU, которая создает один из двух входов в зависимости от состояния флага.cmove r9d, r8d вместо cmp / je сделает это невосприимчивым к ошибочным прогнозам ветвления, но за счет введения зависимости данных от cl и r8d для инструкций, использующих r9d.Процессор Intel не пытается сделать это за вас.

(В Broadwell и более поздних версиях Intel значение cmov составляет всего 1 моп, вместо 2. cmp / jcc равно 1 моп, а сам mov также равен 1 моп, поэтому в невыполненном случае cmov такжеменьше мопов для внешнего интерфейса. И в взятом случае взятая ветвь может привести к появлению пузырьков в конвейере, даже если прогноз правильно, в зависимости от того, насколько высок код пропускной способности кода: могут ли его поглотить очереди между этапами.)

См. Флаг оптимизации gcc -O3 делает код медленнее, чем -O2 , для случая, когда CMOV медленнее, чем ветвь, потому что введение зависимости от данных плохо.

...