Снижается ли производительность при выполнении циклов, количество uop которых не кратно ширине процессора?

мне интересно, как циклы различных размеров выполняют на последних процессорах x86, как функция количества uops.

вот цитата из Питера Кордеса, который поднял вопрос о не кратных-из-4 отсчетов в еще вопрос:

Я также обнаружил, что полоса пропускания uop из буфера цикла не является постоянный 4 за цикл, если цикл не кратен 4 uops. (т. е. это abc, abc,...; не abca, bcab, ...). Агнер туман microarch док к сожалению, не было ясно об этом ограничении буфера цикла.

проблема заключается в том, должны ли циклы быть кратны N uops для выполнения при максимальной пропускной способности uop, где N-ширина процессора. (т. е. 4 для последних процессоров Intel). Есть много осложняющих факторов, когда речь идет о" ширине " и подсчете uops, но я в основном хочу игнорировать их. В частности, не допускайте микро-или макро-слияния.

Питер приводит следующий пример петли с 7 uops в своем теле:

цикл 7-uop выдаст группы в составе 4/3/4/3|... Я не тестировал больше петли (которые не помещаются в буфер цикла), чтобы увидеть, возможно ли первая инструкция из следующей итерации для выпуска в том же группа как взятая ветвь к нему, но я предполагаю, что нет.

в более общем плане утверждение состоит в том, что каждая итерация цикла с x uops в своем теле потребуется не менее ceil(x / 4) итераций, а не просто x / 4.

Это верно для некоторых или всех последних x86-совместимых процессоров?

1 ответов


я провел некоторое расследование с Linux perf чтобы помочь ответить на этот вопрос на моем Skylake с i7-6700HQ получился box, и результаты Haswell были любезно предоставлены другим пользователем. Приведенный ниже анализ применим к Skylake, но за ним следует сравнение с Haswell.

другие архитектуры могут отличаться0 - нам нужно больше тестирования позволяют подтвердить! В этом ключе Я приветствую дополнительные результаты от всех, у кого есть другие архитектуры CPU (источник доступно). Либо добавьте его в этот ответ, либо создайте свой ответ с дополнительными выводами.

этот вопрос в основном касается переднего конца, так как на последних архитектурах это передний конец, который налагает жесткий предел четырех uops плавленого домена за цикл.

сводка правил для производительности цикла

во-первых, я обобщу результаты в терминах нескольких "правил производительности", чтобы иметь в виду при работе с небольшими циклами. Есть много из других правил производительности также-они дополняют их (т. е. вы, вероятно, не нарушаете другое правило, чтобы просто удовлетворить эти).

во-первых, подсчитайте количество макрос-плавленого uops в цикле. Вы можете использовать Агнер это таблицы инструкция чтобы посмотреть это непосредственно для каждой инструкции, за исключением того, что ALU uop и немедленно следовать ветви обычно сливаются в один uop. Тогда основываясь на этом граф:

  • если количество кратно 4, Вы хороши: эти циклы выполняются оптимально.
  • если счет четный и меньше 32, Вы хороши, за исключением того, если это 10, в этом случае вы должны развернуть на другое четное число, если сможете.
  • для нечетных чисел вы должны попытаться развернуть четное число меньше 32 или кратное 4, Если можете.
  • для циклов больше 32 uops, но меньше 64, вы можете развернуть, если это не так уже кратно 4: с более чем 64 uops вы получите эффективную производительность при любом значении на Sklyake и почти все значения на Haswell (с несколькими отклонениями, возможно, связанными с выравниванием). Неэффективность этих циклов все еще относительно невелика: значения, которых следует избегать, -4N + 1 считает, а затем 4N + 2 считается.

краткие выводы

для код подается из УГБ кэша нет явных кратные 4 эффекта. Петли любого числа uops может выполняться при пропускной способности 4 uops плавленого домена за цикл.

для кода, обработанного устаревшими декодерами, верно обратное: время выполнения цикла ограничено целым числом циклов, и, следовательно, циклы, которые не кратны 4 uops, не могут достичь 4 uops/cycle, поскольку они тратят некоторые слоты выпуска/выполнения.

для кода, выданного из детектора потока цикла (LSD), ситуация представляет собой смесь двух ситуаций и объясняется более подробно ниже. В целом, петли менее 32 uops и с четным числом uops выполняются оптимально, в то время как нечетные петли не выполняют, в то время как более крупные петли требуют нескольких из-4 UOP для оптимального выполнения. Подробности ниже.

подробности

как известно любому хорошо разбирающемуся в последних архитектурах x86-64, в любой момент часть выборки и декодирования передней части может работать в нескольких разных режимах, в зависимости от размера кода и других факторов. Как оказалось, эти разные все режимы имеют разное поведение в отношении размера цикла. Я их отдельно покрою.

Устаревший Декодер

на наследие дешифратора1-это полный машинный код к uops декодер, который используется2 когда код не вписывается в механизмы кэширования uop (LSD или DSB). Основная причина этого происходит, если рабочий набор кода больше, чем кэш uop (приблизительно ~1500 uops в идеальном случае, меньше в практика.) Однако для этого теста мы воспользуемся тем, что устаревший декодер также будет использоваться, если выровненный 32-байтовый фрагмент содержит более 18 инструкций3.

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

short_nop:
    mov rax, 100_000_000
ALIGN 32
.top:
    dec rax
    nop
    ...
    jnz .top
    ret

в основном, тривиальный цикл, который отсчитывает до rax равна нулю. Все инструкции являются одним uop4 и ряд nop инструкции разнообразны (на расположение показано как ...), чтобы проверить различные размеры петель (так что 4-UOP петля будет иметь 2 nops, плюс две инструкции по управлению циклом). Нет макро-слияния, поскольку мы всегда отделяем dec и jnz по крайней мере, один nop, а также отсутствие микро-сплавливания. Наконец, нет доступа к памяти (за пределами подразумеваемого доступа icache).

обратите внимание, что этот цикл является очень густые - около 1 байта на инструкцию (начиная с nop инструкции 1 байт каждый) - поэтому мы запустим > 18 инструкций в состоянии 32B chunk, как только попадем в 19 инструкций в цикле. На основе изучения perf счетчики производительности lsd.uops и idq.mite_uops это именно то, что мы видим: по существу 100% инструкций исходят из ЛСД5 до и включая цикл 18 uop, но при 19 uops и выше 100% поступают из устаревшего декодера.

в любом случае, вот циклы / итерации для всех размеров цикла от 3 до 99 uops6:

Cyles/iteration for loops with given size

синие точки-это петли, которые вписываются в ЛСД, и показывают довольно сложное поведение. Мы посмотрим на это позже.

красные точки (начиная с 19 uops / итерации) обрабатываются устаревшим декодером и показывают очень предсказуемый шаблон:

  • все петли с N uops взять именно ceiling(N/4) итераций

Итак, для устаревшего декодера на по крайней мере, наблюдение Питера держится именно на Skylake: петли с кратно 4 uops может выполняться на IPC 4, но любое другое количество uops будет тратить 1, 2 или 3 слота выполнения (для циклов с 4N+3, 4N+2, 4N+1 инструкции, соответственно).

мне непонятно, почему это происходит. Хотя это может показаться очевидным, если вы считаете, что декодирование происходит в непрерывных кусках 16B, и поэтому при скорости декодирования 4 uops / циклических циклов не кратно 4 всегда будет иметь некоторые трейлинг (впустую) слоты в цикле jnz инструкция встречаются. Однако фактическая единица выборки и декодирования состоит из фаз predecode и decode с очередью между ними. Фаза predecode фактически имеет пропускную способность 6 инструкции, но только декодирует до конца 16-байтовой границе на каждом цикле. Это, по-видимому, означает, что пузырь, который происходит в конце цикла, может быть поглощен очередью predecoder - > decode, так как predecoder имеет среднюю пропускную способность выше 4.

поэтому я не могу полностью объяснить это, основываясь на моем понимании того, как работает predecoder. Возможно, существует какое-то дополнительное ограничение в декодировании или предварительном декодировании, которое предотвращает неинтегрированные циклы. Например, возможно, устаревшие декодеры не могут декодировать инструкции с обеих сторон перехода, даже если инструкции после перехода доступны в очереди predecoded. Возможно, это связано с необходимостью дескриптор макрос-фьюжн.

тест выше показывает поведение, когда верхняя часть цикла выровнена по 32-байтовой границе. Ниже приведен тот же график, но с добавленным рядом, который показывает эффект, когда верхняя часть цикла перемещается на 2 байта вверх (i.e, теперь несоосно на границе 32N + 30):

Legacy decoder cycles/iteration when misaligned

большинство размеров петли теперь страдают от штрафа за 1 или 2 цикла. 1 штрафной случай имеет смысл, когда вы рассматриваете декодирование 16B границы и 4-Инструкции за цикл декодирования, а 2 цикла штрафных случаев происходит для циклов, где по какой-то причине DSB используется для 1 инструкции в цикле (вероятно,dec инструкция, которая появляется в своем собственном 32-байтовом чанке), и некоторые штрафы переключения DSBMite понесены.

в некоторых случаях рассогласование не повредит, когда оно в конечном итоге лучше выравнивает конец цикла. Я проверил несоосность, и она сохраняется таким же образом до 200 UOP петель. Если вы возьмите описание предшественников по номиналу, казалось бы, как и выше, они должны уметь скрывать пузырь выборки для рассогласования, но этого не происходит (возможно, очередь недостаточно велика).

DSB (UOP кэш)

кэш uop (Intel любит называть его DSB) способен кэшировать большинство циклов умеренного количества инструкций. В типичной программе, вы надеетесь, что большинство ваших инструкций подаются из этого кэша7.

мы можем повторить тест выше, но теперь обслуживаем uops из кэша uop. Это простой вопрос увеличения размера наших nops до 2 байт, поэтому мы больше не достигаем предела 18-инструкции. Мы используем 2-байтовый nop xchg ax, ax в нашем цикле:

long_nop_test:
    mov rax, iters
ALIGN 32
.top:
    dec eax
    xchg ax, ax  ; this is a 2-byte nop
    ...
    xchg ax, ax
    jnz .top
    ret

здесь результаты очень просты. Для всех испытанных размеров петли поставленных из DSB, число требуемых циклов было N/4 - т. е. петли, выполняемые при максимальной теоретической пропускной способности, даже если у них не было кратно 4 uops. Таким образом, в целом, на Skylake, петли среднего размера, обслуживаемые из DSB, не должны беспокоиться о том, что количество uop соответствует определенному множеству.

вот график из 1000 циклов uop. Если вы прищуритесь, вы можете увидеть неоптимальное поведение перед 64-uops (когда цикл находится в ЛСД). После этого это прямой выстрел, 4 IPC весь путь до 1,000 uops (с точкой около 900, которая, вероятно, была вызвана нагрузкой на мой box):

Cycle counts for loops served out of the DSB

Далее мы рассмотрим производительность для циклов, которые достаточно малы, чтобы поместиться в кэше uop.

ЛСД (детектор пара петли)

важное замечание: Intel, по-видимому,отключен ЛСД на Skylake (skl150 erratum) и Kaby Lake (kbl095, KBW095 erratum) чипы через обновление микрокода и на Skylake-X из коробки, из-за ошибка связанных с взаимодействие между многопоточность и ЛСД. Для этих чипов на графике ниже, скорее всего, не будет интересного региона до 64 uops; скорее, он будет выглядеть так же, как регион после 64 uops.

детектор потока петли может кэшировать небольшие петли до 64 uops (на Skylake). В недавней документации Intel он позиционируется больше как механизм энергосбережения, чем функция производительности, хотя, конечно, нет недостатков производительности, упомянутых в использовании ЛСД.

запуск этого для размеров цикла, которые должны соответствовать ЛСД, мы получаем следующее поведение циклов / итераций:

Cycles per Iteration for LSD-resident loops

красная линия здесь-это % uops, которые доставляются из ЛСД. Оно flatlines на 100% для всех размеров петли от 5 до 56 uops.

для 3 и 4 циклов uop у нас есть необычное поведение, что 16% и 25% UOP, соответственно, поставляются из устаревшего декодера. А? К счастью, это, похоже, не влияет на пропускную способность цикла, поскольку оба случая достигают максимальной пропускной способности 1 цикла/цикла - несмотря на то, что можно было ожидать некоторых штрафов перехода MITELSD.

между размерами петель 57 и 62 uops количество UOP, доставленных из ЛСД, демонстрирует некоторое странное поведение-приблизительно 70% uops доставляются из ЛСД, а остальные из DSB. Skylake номинально имеет 64-uop ЛСД, так что это своего рода переход прямо перед ЛСД размер превышен - возможно, в IDQ (на котором реализован ЛСД) есть какая-то внутренняя настройка, которая вызывает только частичные попадания в ЛСД на этой фазе. Эта фаза коротка и, с точки зрения производительности, кажется в основном линейной комбинацией полной производительности в ЛСД, которая предшествует ей, и полной производительности в DSB, которая следует за ней.

давайте посмотрим на основную часть результатов между 5 и 56 uops. Мы видим три различных регионы:

петли от 3 до 10 uops: здесь поведение сложное. Это единственная область, где мы видим количество циклов, которое не может быть объяснено статическим поведением за одну итерацию цикла8. Диапазон достаточно короткий, что трудно сказать, есть ли шаблон. Петли 4, 6 и 8 uops все выполняются оптимально, в N/4 циклы (это тот же шаблон, что и следующая область).

цикл из 10 uops, с другой стороны, выполняется в 2.66 циклов на итерацию, что делает его единственным четным размером цикла, который не выполняется оптимально, пока вы не получите размеры цикла 34 uops или выше (кроме выбросов в 26). Это соответствует чему-то вроде повторной скорости выполнения uop/cycle 4, 4, 4, 3. Для цикла 5 uops вы получаете 1.33 цикла на итерацию, очень близко, но не то же самое, что идеал 1.25. Это соответствует скорости выполнения 4, 4, 4, 4, 3.

я не могу объяснить эти результаты, кроме как сказать, что есть происходит что-то сложное, возможно, unro. Результаты повторяются от запуска к запуску и надежны для изменений, таких как замена nop на инструкцию, которая фактически делает что-то вроде mov ecx, 123.

петли от 11 до 32-uops: мы видим шаблон ступеньки лестницы, но с периодом два. В основном все петли с даже количество uops выполнять оптимально-то есть, принимая точно N/4 циклы. Петли с нечетным числом uops waste one " проблема слот", и взять такое же количество циклов, как цикл с еще одним uops (т. е. цикл 17 uop принимает те же 4,5 цикла, что и цикл 18 uop). Так что здесь мы имеем поведение лучше, чем ceiling(N/4) для многих подсчетов uop, и у нас есть первое доказательство того, что Skylake по крайней мере может выполнять петли в нецелом числе циклов.

единственными выбросами являются N=25 и N=26, которые занимают около 1,5% больше времени, чем ожидалось. Он маленький, но воспроизводимые и надежные для перемещения функции в файл. Это слишком мало для объяснения эффекта на итерацию, если только у него нет гигантского периода, поэтому это, вероятно, что-то еще.

общее поведение здесь точно согласуется (за пределами аномалии 25/26) с оборудованием разворачивание цикла в 2 раза.

петли от 33 до ~64 uops: мы снова видим шаблон лестницы, но с периодом 4 и худшей средней производительностью, чем в случае до 32 uop. Поведение это точно ceiling(N/4) - то есть то же самое, что и в случае устаревшего декодера. Таким образом, для циклов от 32 до 64 uops ЛСД не дает видимой выгоды по сравнению с устаревшими декодерами, с точки зрения пропускной способности переднего плана для этого конкретного ограничения. Конечно, есть много других способов улучшить ЛСД - он избегает многих потенциальных узких мест декодирования, которые возникают для более сложных или длинных инструкций, и экономит энергию и т. д.

все это довольно удивительно, потому что означает, что циклы, доставленные из кэша uop, обычно выполняют лучше в передней части, чем петли, поставляемые из ЛСД, несмотря на то, что ЛСД обычно позиционируется как строго лучший источник uops, чем DSB (например, как часть совета, чтобы попытаться сохранить петли достаточно маленькими, чтобы поместиться в ЛСД).

вот еще один способ взглянуть на те же данные - с точки зрения потери эффективности для данного количества uop по сравнению с теоретической максимальной пропускной способностью 4 UOP за цикл. A 10% эффективность хита означает, что у вас есть только 90% пропускной способности, которую вы рассчитаете из простого N/4 формула.

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

Efficiency Loss by Loop Size

три области, рассмотренные выше, окрашены по-разному, и, по крайней мере, конкурирующие эффекты видно:

  1. при прочих равных условиях, чем больше количество участвующих uops, тем ниже эффективность удара. Хит является фиксированной стоимостью только один раз за итерацию, поэтому большие петли платят меньше относительные стоимость.

  2. существует большой скачок в неэффективности при переходе в область 33+ uop: как размер потери пропускной способности увеличивается, так и количество затронутых подсчетов uop удваивает.

  3. первая область несколько хаотична, и 7 uops-худшее общее количество uop.

трассы

анализ DSB и LSD выше предназначен для записей цикла, выровненных по 32-байтовой границе, но не выровненный случай, похоже, не страдает ни в том, ни в другом случае: нет существенной разницы от выровненного случая (кроме, возможно, небольшого изменения менее 10 uops, которые я не исследовал дальнейший.)

вот несогласованные результаты для 32N-2 и 32N+2 (т. е. верхний цикл 2 байта до и после границы 32B):

Misaligned Cycles per Iteration

идеал N/4 линия также показана для справки.

долларов

далее далее взгляните на предыдущую микроархитектуру: Haswell. Номера здесь были любезно предоставлены пользователем Iwillnotexist Idonotexist.

LSD + Legacy Decode Pipeline

во-первых, результаты теста "плотный код", который проверяет ЛСД (для небольших подсчетов uop) и устаревший конвейер (для больших подсчетов uop, так как цикл "выбрасывает" DSB из-за плотности инструкций.

сразу же мы видим разницу уже с точки зрения , когда каждая архитектура обеспечивает uops от ЛСД для плотного цикла. Ниже мы сравниваем Skylake и Haswell для краткости петли густые код (1 байт на инструкцию).

Haswell vs Skylake LSD Delivery %

как описано выше, цикл Skylake перестает доставляться из ЛСД ровно на 19 uops, как и ожидалось от 18-uop на 32-байтовую область ограничения кода. Хасвелл, с другой стороны, кажется, перестает надежно доставлять ЛСД для 16-uop и 17-uop петель. У меня нет никакого объяснения этому. Существует также разница в случае 3-uop: как ни странно, оба процессоры только доставить некоторые из их uops из ЛСД в 3 и 4 случаях uop, но точное количество одинаково для 4 uops и отличается от 3.

мы в основном заботимся о фактической производительности, верно? Итак, давайте посмотрим на циклы/итерации для 32-байтовое выравнивание густые код:

Haswell vs Skylake LSD + Legacy Pipeline

это те же данные, что и показано выше для Skylake (несоосная серия снято), с Хасвеллом рядом. Сразу же вы заметите, что шаблон как для Haswell, но не то же самое. Как и выше, здесь есть две области:

Legacy Decode

петли, большие, чем ~16-18 uops (неопределенность описана выше), доставляются из устаревших декодеров. Модель для Haswell несколько отличается от Skylake.

для диапазона от 19-30 uops они идентичны, но после этого Haswell ломает шаблон. Скайлэйк взял ceil(N/4) циклы для циклов, доставленных из устаревших декодеров. С другой стороны, хасвелл, кажется, принимает что-то вроде ceil((N+1)/4) + ceil((N+2)/12) - ceil((N+1)/12). Хорошо, это грязно (короче, кто-нибудь?)- но в основном это означает, что в то время как Skylake выполняет циклы с 4*n циклами оптимально (i.e,. при 4-uops / cycle) такие петли (локально) обычно являются меньше оптимальное количество (по крайней мере локально) - для выполнения таких циклов требуется еще один цикл, чем Skylake. Так что ты на самом деле лучший выкл с петлями 4N-1 uops на Haswell,за исключением что 25% таких петель, которые и формы 16-1Н (31, 47, 63, и т. д) возьмите один дополнительный цикл. Это начинает звучать как расчет високосного года , но шаблон, вероятно, лучше всего понять визуально выше.

я не думаю, что этот шаблон внутренние в UOP dispatch на Haswell, поэтому мы не должны много читать. Вроде бы объяснил

0000000000455a80 <short_nop_aligned35.top>:
16B cycle
  1     1 455a80:       ff c8   dec    eax
  1     1 455a82:       90      nop
  1     1 455a83:       90      nop
  1     1 455a84:       90      nop
  1     2 455a85:       90      nop
  1     2 455a86:       90      nop
  1     2 455a87:       90      nop
  1     2 455a88:       90      nop
  1     3 455a89:       90      nop
  1     3 455a8a:       90      nop
  1     3 455a8b:       90      nop
  1     3 455a8c:       90      nop
  1     4 455a8d:       90      nop
  1     4 455a8e:       90      nop
  1     4 455a8f:       90      nop
  2     5 455a90:       90      nop
  2     5 455a91:       90      nop
  2     5 455a92:       90      nop
  2     5 455a93:       90      nop
  2     6 455a94:       90      nop
  2     6 455a95:       90      nop
  2     6 455a96:       90      nop
  2     6 455a97:       90      nop
  2     7 455a98:       90      nop
  2     7 455a99:       90      nop
  2     7 455a9a:       90      nop
  2     7 455a9b:       90      nop
  2     8 455a9c:       90      nop
  2     8 455a9d:       90      nop
  2     8 455a9e:       90      nop
  2     8 455a9f:       90      nop
  3     9 455aa0:       90      nop
  3     9 455aa1:       90      nop
  3     9 455aa2:       90      nop
  3     9 455aa3:       75 db   jne    455a80 <short_nop_aligned35.top>

здесь я отметил фрагмент декодирования 16B (1-3), в котором появляется каждая инструкция, и цикл, в котором она будет декодирована. Правило в основном состоит в том, что до следующих 4 инструкций декодируются, если они попадают в текущий 16b кусок. Иначе им придется ждать следующего цикла. Для N=35 мы видим, что в цикле 4 есть потеря 1 слота декодирования (в куске 16B осталось только 3 инструкции), но в противном случае цикл очень хорошо согласуется с границами 16B и даже последний цикл (9) может декодировать 4 Инструкции.

вот усеченный взгляд на N=36, который идентичен, за исключением конца цикла:

0000000000455b20 <short_nop_aligned36.top>:
16B cycle
  1     1 455a80:       ff c8   dec    eax
  1     1 455b20:       ff c8   dec    eax
  1     1 455b22:       90      nop
  ... [29 lines omitted] ...
  2     8 455b3f:       90      nop
  3     9 455b40:       90      nop
  3     9 455b41:       90      nop
  3     9 455b42:       90      nop
  3     9 455b43:       90      nop
  3    10 455b44:       75 da   jne    455b20 <short_nop_aligned36.top>

теперь есть 5 инструкций для декодирования в 3-м и заключительном 16b куске, поэтому необходим один дополнительный цикл. В основном 35 инструкции для этого конкретного шаблона инструкций случается выстраиваться лучше с 16b-битными границами и сохраняет один цикл при декодировании. Это не значит, что N=35 лучше, чем N=36 в целом! Различные инструкции будут иметь разное количество байтов и будут выстраиваться по-разному. Аналогичная проблема выравнивания объясняет также дополнительный цикл, который требуется каждые 16 байт:

16B cycle
...
  2     7 45581b:       90      nop
  2     8 45581c:       90      nop
  2     8 45581d:       90      nop
  2     8 45581e:       90      nop
  3     8 45581f:       75 df   jne    455800 <short_nop_aligned31.top>

вот финал jne проскользнул в следующий кусок 16B (если инструкция охватывает границу 16B, она эффективно находится в последнем куске), вызывая дополнительную потерю цикла. Это происходит только каждые 16 байт.

Итак, наследие Haswell результаты декодера прекрасно объясняются устаревшим декодером, который ведет себя так, как описано, например, в Agner Fog микроархитектура doc. Фактически, это также объясняет результаты Skylake, если вы предполагаете, что Skylake может декодировать 5 инструкций за цикл (доставка до 5 uops)9. Предполагая, что это возможно, асимптотическое наследие декодирует пропускную способность на этот код для Skylake все еще 4-uops, так как блок из 16 nops декодирует 5-5-5-1, против 4-4-4-4 на Haswell, таким образом, вы получаете преимущества только по краям: в случае N=36 выше, например, Skylake может декодировать все оставшиеся 5 инструкций против 4-1 для Haswell, экономя цикл.

в результате кажется, что поведение устаревшего декодера можно понять довольно просто, и главный совет по оптимизации-продолжать массировать код, чтобы он "умно" попадал в выровненные куски 16B (возможно, это NP-жесткий, как упаковка в бункер?).

DSB (и Снова ЛСД)

Далее давайте рассмотрим сценарий, в котором код подается из ЛСД или DSB - с помощью теста "long nop", который позволяет избежать нарушения 18-uop на 32B chunk limit и поэтому остается в DSB.

Haswell vs Skylake:

Haswell vs Skylake LSD and DSB

обратите внимание на поведение ЛСД - здесь Хасвелл прекращает подачу ЛСД ровно на 57 uops, что полностью соответствует опубликованному размеру ЛСД 57 uops. Нет странного "переходного периода", как мы видим на Skylake. Haswell также имеет странное поведение для 3 и 4 uops, где только ~0% и ~40% uops, соответственно, происходят от ЛСД.

С точки зрения производительности, Haswell обычно соответствует Skylake с несколькими отклонениями, например, около 65, 77 и 97 uops, где он округляется до следующего цикла, тогда как Skylake всегда может поддерживать 4 uops/цикл, даже если это приводит к нецелому числу циклов. Небольшое отклонение от ожидалось, что в 25 и 26 uops исчезнет. Возможно, скорость доставки 6-uop Skylake помогает избежать проблем выравнивания UOP-cache, которые Haswell страдает от скорости доставки 4-uop.

Требуются

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


0 в частности, максимальная пропускная способность устаревшего декодера, по-видимому, увеличилась с 4 до 5 uops в Skylake, а максимальная пропускная способность для кэша uop увеличилась с 4 до 6. Оба из них могут повлиять на результаты, описанные здесь.

1 Intel на самом деле нравится называть устаревший декодер MITE (Micro-instruction Translation Engine), возможно, потому, что это faux-pas, чтобы фактически пометить любую часть вашего архитектура с наследие коннотацию.

2 технически существует другой, еще более медленный источник uops-MS (механизм секвенирования микрокода), который используется для реализации любой инструкции с более чем 4 uops, но мы игнорируем это здесь, поскольку ни один из наших циклов не содержит микрокодированных инструкций.

3 это работает, потому что любой выровненный 32-байтовый кусок может использовать не более 3-способов в своем слоте кэша uop, и каждый слот удерживает до 6 uops. Поэтому, если вы используете больше, чем 3 * 6 = 18 uops в блоке 32B код вообще не может быть сохранен в кэше uop. Вероятно, редко встречается это условие на практике, так как код должен быть очень плотным (менее 2 байт на инструкцию), чтобы вызвать это.

4 на nop инструкции декодируются до одного uop, но не исключаются до выполнения (т. е. они не используют порт выполнения) - но все равно занимают место в переднем конце и поэтому подсчитывают против различных пределов, которые нас интересуют.

5 ЛСД-это детектор потока петли, который кэширует небольшие петли до 64 (Skylake) uops непосредственно в IDQ. На более ранних архитектурах он может содержать 28 uops (оба логических ядра активны) или 56 uops (одно логическое ядро активно).

6 мы не можем легко вписать цикл 2 uop в этот шаблон, так как это будет означать ноль nop инструкции, т. е. dec и jnz инструкции будут макро-предохранителем с соответствующим изменением количества uop. Просто поверьте мне на слово, что все циклы с 4 или менее uops выполняются в лучшем случае на 1 цикле/итерации.

7 для удовольствия, я просто побежал perf stat против короткого запуска Firefox, где я открыл вкладку и щелкнул по нескольким вопросам переполнения стека. Для получения инструкций я получил 46% от DSB, 50% от legacy decoder и 4% для LSD. Это показывает, что по крайней мере для большого, ветвистого кода, такого как браузер DSB по-прежнему не может захватить большую часть кода (к счастью, устаревшие декодеры не так уж плохи).

8 этим я имею в виду, что все остальные подсчеты циклов можно объяснить просто путем принятия "эффективной" стоимости интегрального цикла в uops (которая может быть выше, чем фактический размер uops) и деления на 4. Для этих очень коротких циклов это не работает - вы не можете получить 1.333 цикла на итерацию, разделив любое целое число на 4. Иначе говоря, во всех в других регионах затраты имеют вид N / 4 для некоторого целого числа N.

9 на самом деле мы знаем, что Skylake можете поставьте 5 uops в цикл от дешифратора наследия, но мы не знаем если те 5 uops могут прийти от 5 различных инструкций, или только 4 или. То есть, мы ожидаем, что Skylake может декодировать в шаблоне 2-1-1-1, но я не уверен, что он может декодировать в шаблоне 1-1-1-1-1. Вышеуказанные результаты дают некоторые доказательства того, что он действительно может декодировать 1-1-1-1-1.