Не существует особых случаев для коротких расстояний между ветвями в любых процессорах 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 медленнее, чем ветвь, потому что введение зависимости от данных плохо.