Режимы микросовключения и адресации

Я нашел что-то неожиданное (для меня) с помощью анализатора кода архитектуры Intel® (IACA).

Следующая инструкция, использующая [base+index] адресация

addps xmm1, xmmword ptr [rsi+rax*1]

не микро-предохранитель в соответствии с IACA. Однако, если я использую [base+offset], как этот

addps xmm1, xmmword ptr [rsi]

IACA сообщает, что он плавкий предохранитель.

В Разделе 2-11 справочного руководства по оптимизации Intel приводятся следующие примеры: "Микропроцессорные микрооперации, которые можно обрабатывать всеми декодерами"

FADD DOUBLE PTR [RDI + RSI*8]

и Руководство по сборке оптимизации Agner Fog также приводят примеры слияния микроопераций с использованием адресации [base+index]. См., Например, раздел 12.2 "Тот же пример для Core2". Итак, какой правильный ответ?

Ответ 1

В декодерах и uop-кеше режим адресации не влияет на микро-fusion (за исключением того, что команда с непосредственным операндом не может скомпоновать RIP-относительный режим адресации).

Но некоторые комбинации режима uop и адресации не могут оставаться микро-сплавленными в ROB (в ядре вне порядка), поэтому процессоры Intel SnB-семейства "не ламинируют", когда это необходимо, в какой-то момент этап выпуска/переименования. Для размера пропускной способности и размера окна вне порядка (размер ROB) важно, чтобы процент упсонов с плавной долей после не-ламинирования был важным.

Руководство по оптимизации Intel описывает неламинирование для Sandybridge в разделе 2.3.2.4: Micro-op Queue и Loop Stream Detector (LSD), но не описывает изменения для последующих микроархитектур.


Правила, насколько я могу судить по экспериментам на SnB, HSW и SKL:

  • SnB (и я предполагаю также IvB): индексированные режимы адресации всегда не ламинируются, другие остаются микроплавками. IACA (в основном?) Правильный.
  • HSW, SKL: они содержат только индексированную инструкцию ALU с микросоединением, если она имеет 2 операнда и обрабатывает регистр dst как read-modify-write. Здесь "операнды" включают в себя флаги, означающие, что adc и cmov не являются микро-предохранителями. Большинство VEX-закодированных инструкций также не сливаются, поскольку они обычно имеют три операнда (поэтому paddb xmm0, [rdi+rbx] плавкие предохранители, но vpaddb xmm0, xmm0, [rdi+rbx] нет). Наконец, случайная инструкция с двумя операндами, где только первый операнд пишет только, например pabsb xmm0, [rax + rbx], также не сливается. IACA ошибается, применяя правила SnB.

Связанные: простые (неиндексированные) режимы адресации являются единственными, которые могут обрабатывать выделенный блок хранения-адреса на порту7 (Хасуэлл и позже), поэтому по-прежнему полезно избегать индексированных режимов адресации для магазинов. (Хорошим трюком для этого является обращение к вашему dst с помощью одного регистра, но src с dst+(initial_src-initial_dst). Тогда вам нужно только увеличить регистр dst внутри цикла.)

Обратите внимание, что некоторые инструкции никогда не микро-предохранители вообще (даже в декодерах/uop-кеш). например shufps xmm, [mem], imm8, или vinsertf128 ymm, ymm, [mem], imm8, всегда являются 2 uops на SnB через Skylake, хотя их версии с реестром-исходным кодом составляют всего 1 мкп. Это типично для инструкций с управляющим операндом imm8 плюс обычные операнды register/memory dest/src1, src2, но есть несколько других случаев. например PSRLW/D/Q xmm,[mem] (счетчик сдвига вектора из операнда памяти) не замачивается, а также не PMULLD.

См. также этот пост в блоге Agner Fog для обсуждения ограничений пропускной способности выпуска на HSW/SKL, когда вы читаете множество регистров: Многое микро-слияние с режимами индексированной адресации может привести к замедлению по сравнению с теми же инструкциями с меньшим количеством операндов регистров: режимы однорежимной адресации и немедленно. Мы еще не знаем причину, но я подозреваю, что какой-то вид предела чтения регистра, возможно, связано с чтением большого количества холодных регистров из ПРФ.


Тестовые примеры, цифры от реальных измерений: все эти микроплавкие в декодерах, AFAIK, даже если они позже не ламинируются.

# store
mov        [rax], edi  SnB/HSW/SKL: 1 fused-domain, 2 unfused.  The store-address uop can run on port7.
mov    [rax+rsi], edi  SnB: unlaminated.  HSW/SKL: stays micro-fused.  (The store-address can't use port7, though).
mov [buf +rax*4], edi  SnB: unlaminated.  HSW/SKL: stays micro-fused.

# normal ALU stuff
add    edx, [rsp+rsi]  SnB: unlaminated.  HSW/SKL: stays micro-fused.  
# I assume the majority of traditional/normal ALU insns are like add

Инструкции с тремя входами, которые HSW/SKL, возможно, придется не ламинировать

vfmadd213ps xmm0,xmm0,[rel buf] HSW/SKL: stays micro-fused: 1 fused, 2 unfused.
vfmadd213ps xmm0,xmm0,[rdi]     HSW/SKL: stays micro-fused
vfmadd213ps xmm0,xmm0,[0+rdi*4] HSW/SKL: un-laminated: 2 uops in fused & unfused-domains.
     (So indexed addressing mode is still the condition for HSW/SKL, same as documented by Intel for SnB)

# no idea why this one-source BMI2 instruction is unlaminated
# It different from ADD in that its destination is write-only (and it uses a VEX encoding)
blsi   edi, [rdi]       HSW/SKL: 1 fused-domain, 2 unfused.
blsi   edi, [rdi+rsi]   HSW/SKL: 2 fused & unfused-domain.


adc         eax, [rdi] same as cmov r, [rdi]
cmove       ebx, [rdi]   Stays micro-fused.  (SnB?)/HSW: 2 fused-domain, 3 unfused domain.  
                         SKL: 1 fused-domain, 2 unfused.

# I haven't confirmed that this micro-fuses in the decoders, but I'm assuming it does since a one-register addressing mode does.

adc   eax, [rdi+rsi] same as cmov r, [rdi+rsi]
cmove ebx, [rdi+rax]  SnB: untested, probably 3 fused&unfused-domain.
                      HSW: un-laminated to 3 fused&unfused-domain.  
                      SKL: un-laminated to 2 fused&unfused-domain.

Я предполагаю, что Бродвелл ведет себя как Skylake для adc/cmov.

Странно, что HSW не ламинирует АЦП с памятью и CMOV. Возможно, Intel не оборачивается, чтобы изменить это от SnB до того, как они наберут крайний срок для доставки Haswell.

В таблице Agner insn говорится, что cmovcc r,m и adc r,m вообще не замаскированы на HSW/SKL, но это не соответствует моим экспериментам. Количество циклов, в которые я измеряю, совпадает с количеством ошибок uop для объединенных доменов, для узкого места с 4-мя часами/часами. Надеюсь, он дважды проверит это и исправьте таблицы.

Целое число памяти ALU:

add        [rdi], eax  SnB: untested (Agner says 2 fused-domain, 4 unfused-domain (load + ALU  + store-address + store-data)
                       HSW/SKL: 2 fused-domain, 4 unfused.
add    [rdi+rsi], eax  SnB: untested, probably 4 fused & unfused-domain
                       HSW/SKL: 3 fused-domain, 4 unfused.  (I don't know which uop stays fused).
                  HSW: About 0.95 cycles extra store-forwarding latency vs. [rdi] for the same address used repeatedly.  (6.98c per iter, up from 6.04c for [rdi])
                  SKL: 0.02c extra latency (5.45c per iter, up from 5.43c for [rdi]), again in a tiny loop with dec ecx/jnz


adc     [rdi], eax      SnB: untested
                        HSW: 4 fused-domain, 6 unfused-domain.  (same-address throughput 7.23c with dec, 7.19c with sub ecx,1)
                        SKL: 4 fused-domain, 6 unfused-domain.  (same-address throughput ~5.25c with dec, 5.28c with sub)
adc     [rdi+rsi], eax  SnB: untested
                        HSW: 5 fused-domain, 6 unfused-domain.  (same-address throughput = 7.03c)
                        SKL: 5 fused-domain, 6 unfused-domain.  (same-address throughput = ~5.4c with sub ecx,1 for the loop branch, or 5.23c with dec ecx for the loop branch.)

Да, это право, adc [rdi],eax/dec ecx/jnz работает быстрее, чем тот же цикл с add вместо adc в SKL. Я не пытался использовать разные адреса, так как ясно, что SKL не любит повторные перезаписи одного и того же адреса (латентность пересылки в хранилище выше ожидаемой. См. Также this сообщение о повторном хранении/перезагрузке на тот же адрес, который медленнее, чем ожидалось, в SKL.

Память-назначения adc - это так много, потому что семейство Intel P6 (и, по-видимому, SnB-семейство) не может хранить одни и те же записи TLB для всех uops команды multi-uop, поэтому нужен дополнительный uop для решения проблемного случая, когда загрузка и добавление завершаются, а затем ошибки хранилища, но insn нельзя просто перезапустить, потому что CF уже обновлен. Интересная серия комментариев Энди Глева (@krazyglew).

Предположительно, слияние в декодерах и без ламинирования позже избавит нас от необходимости микрокода ROM, чтобы создать более 4 объединенных доменов из одной команды для adc [base+idx], reg.


Почему SnB-семья не ламинирует:

Sandybridge упростил внутренний формат uop для экономии энергии и транзисторов (наряду с существенным изменением использования файла физического регистра, вместо того, чтобы хранить данные ввода/вывода в ROB). Сетевые процессоры SnB допускают только ограниченное количество входных регистров для uop с плавным доменом в ядре вне порядка. Для SnB/IvB этот предел - 2 входа (включая флаги). Для HSW и позже предел составляет 3 входа для uop. Я не уверен, что использование памяти-назначения add и adc в полной мере использует это, или если Intel должна была заставить Хасуэлл выйти из двери с некоторыми инструкциями

Nehalem и ранее имеют предел в 2 входа для uop неустановленной области, но ROB, по-видимому, может отслеживать микро-fused uops с 3 входными регистрами (операндом, базой и индексом без памяти).


Таким образом, индексированные магазины и инструкции по загрузке ALU + могут эффективно декодироваться (не обязательно быть первым uop в группе) и не занимать лишнее пространство в кэше uop, но в остальном преимущества микро-слияния в основном пошли для настройки плотных петель. "не-ламинирование" происходит до того, как 4-fused-domain-uops-per-cycle выдает/удаляет ширину из-за отсутствия ядра. Счетчики производительности с плавным доменом (uops_issued/uops_retired.retire_slots) подсчитывают количество подключенных доменов после отказа.

Intel-описание переименования (раздел 2.3.3.1: Renamer) подразумевает, что это этап выпуска/переименования, который на самом деле делает неламинирование, поэтому uops, предназначенные для не-ламинирования, все еще могут быть микроплавками в 28/56/64 queue queue/loop-buffer foo-domain (также известный как IDQ).

TODO: проверьте это. Создайте цикл, который должен просто сместиться в буфере цикла. Измените что-нибудь, чтобы один из uops был нерасширен до выдачи, и посмотрите, все ли он выполняется из буфера цикла (LSD), или если все uops теперь повторно выбраны из кеша uop (DSB). Есть перфорированные счетчики, чтобы отслеживать, откуда приходит uops, поэтому это должно быть легко.

Harder TODO: если между чтением из кеша uop и добавлением IDQ происходит неламинирование, проверьте, может ли он когда-либо уменьшить пропускную способность uop-cache. Или, если на стадии эмиссии не происходит ламинация, может ли это повредить пропускную способность? (то есть как он обрабатывает оставшиеся uops после выдачи первых 4.)


(см. предыдущую версию этого ответа для некоторых догадок, основанных на настройке некоторого кода LUT, причем некоторые примечания на vpgatherdd составляют около 1.7x больше циклов, чем цикл pinsrw.)

Экспериментальное тестирование на SnB

Номера HSW/SKL были измерены на i5-4210U и i7-6700k. Оба поддерживали HT (но система простаивала, так что поток имел все ядро ​​для себя). Я запускал те же статические двоичные файлы в обеих системах, Linux 4.10 на SKL и Linux 4.8 на HSW, используя ocperf.py. (Ноутбук HSW NFS установлен на моем рабочем столе SKL/home.)

Номера SnB были измерены, как описано ниже, на i5-2500k, который больше не работает.

Подтверждено тестированием с помощью счетчиков производительности для циклов и циклов.

Я нашел таблицу событий PMU для Intel Sandybridge, для использования с командой Linux perf. (Стандарт perf, к сожалению, не имеет символических имен для большинства аппаратных событий PMU, таких как uops.) Я использовал его для недавнего ответа.

ocperf.py предоставляет символические имена для этих событий PMU, связанных с uarch, поэтому вам не нужно искать таблицы. Кроме того, одно и то же символическое имя работает через несколько урчей. Я не знал об этом, когда впервые написал этот ответ.

Чтобы проверить микропроцессор uop, я построил тестовую программу, которая была узкополостной на пределе FED-диапазона для процессоров Intel. Чтобы избежать конкуренции с исполнением порта, многие из этих uops составляют nop s, которые все еще находятся в кэше uop и проходят через конвейер так же, как и любой другой uop, за исключением того, что они не отправляются в порт выполнения. (An xor x, same или исключенный ход будут одинаковыми.)

Программа тестирования: yasm -f elf64 uop-test.s && ld uop-test.o -o uop-test

GLOBAL _start
_start:
    xor eax, eax
    xor ebx, ebx
    xor edx, edx
    xor edi, edi
    lea rsi, [rel mydata]   ; load pointer
    mov ecx, 10000000
    cmp dword [rsp], 2      ; argc >= 2
    jge .loop_2reg

ALIGN 32
.loop_1reg:
    or eax, [rsi + 0]
    or ebx, [rsi + 4]
    dec ecx
    nop
    nop
    nop
    nop
    jg .loop_1reg
;   xchg r8, r9     ; no effect on flags; decided to use NOPs instead

    jmp .out

ALIGN 32
.loop_2reg:
    or eax, [rsi + 0 + rdi]
    or ebx, [rsi + 4 + rdi]
    dec ecx
    nop
    nop
    nop
    nop
    jg .loop_2reg

.out:
    xor edi, edi
    mov eax, 231    ;  exit(0)
    syscall

SECTION .rodata
mydata:
db 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff

Я также обнаружил, что ширина канала uop из буфера цикла не является постоянной 4 за цикл, если цикл не является кратным 4 мкп. (т.е. abc, abc,..., not abca, bcab,...). Agner Fog microarch doc, к сожалению, не был ясен в этом ограничении буфера цикла. См. Снижается ли производительность при выполнении циклов, чей счетчик uop не является кратным ширине процессора? для более подробного изучения HSW/SKL. В этом случае SnB может быть хуже HSW, но я не уверен, и у меня пока нет работы с оборудованием SnB.

Я хотел сохранить макро-fusion (compare-and-branch) из изображения, поэтому я использовал nop между dec и веткой. Я использовал 4 nop s, поэтому с микро-слиянием цикл был бы 8 uops и заполнял бы трубопровод с 2 циклами на 1 итерацию.

В другой версии цикла, используя режимы адресации с двумя операндами, которые не имеют микро-предохранителей, цикл будет состоять из 10 плагированных доменов и будет выполняться в 3 цикла.

Результаты моего 3,3-гигабайтного Intel Sandybridge (i5 2500k). Я ничего не делал, чтобы заставить контроллер cpufreq увеличивать тактовую частоту до тестирования, поскольку циклы - это циклы, когда вы не взаимодействуя с памятью. Я добавил аннотации к событиям счетчика производительности, которые я должен был ввести в hex.

тестирование режима адресации 1-reg: no cmdline arg

$ perf stat -e task-clock,cycles,instructions,r1b1,r10e,r2c2,r1c2,stalled-cycles-frontend,stalled-cycles-backend ./uop-test

Performance counter stats for './uop-test':

     11.489620      task-clock (msec)         #    0.961 CPUs utilized
    20,288,530      cycles                    #    1.766 GHz
    80,082,993      instructions              #    3.95  insns per cycle
                                              #    0.00  stalled cycles per insn
    60,190,182      r1b1  ; UOPS_DISPATCHED: (unfused-domain.  1->umask 02 -> uops sent to execution ports from this thread)
    80,203,853      r10e  ; UOPS_ISSUED: fused-domain
    80,118,315      r2c2  ; UOPS_RETIRED: retirement slots used (fused-domain)
   100,136,097      r1c2  ; UOPS_RETIRED: ALL (unfused-domain)
       220,440      stalled-cycles-frontend   #    1.09% frontend cycles idle
       193,887      stalled-cycles-backend    #    0.96% backend  cycles idle

   0.011949917 seconds time elapsed

тестирование режима адресации 2-reg: с помощью cmdline arg

$ perf stat -e task-clock,cycles,instructions,r1b1,r10e,r2c2,r1c2,stalled-cycles-frontend,stalled-cycles-backend ./uop-test x

 Performance counter stats for './uop-test x':

         18.756134      task-clock (msec)         #    0.981 CPUs utilized
        30,377,306      cycles                    #    1.620 GHz
        80,105,553      instructions              #    2.64  insns per cycle
                                                  #    0.01  stalled cycles per insn
        60,218,693      r1b1  ; UOPS_DISPATCHED: (unfused-domain.  1->umask 02 -> uops sent to execution ports from this thread)
       100,224,654      r10e  ; UOPS_ISSUED: fused-domain
       100,148,591      r2c2  ; UOPS_RETIRED: retirement slots used (fused-domain)
       100,172,151      r1c2  ; UOPS_RETIRED: ALL (unfused-domain)
           307,712      stalled-cycles-frontend   #    1.01% frontend cycles idle
         1,100,168      stalled-cycles-backend    #    3.62% backend  cycles idle

       0.019114911 seconds time elapsed

Итак, обе версии выполнили 80M-инструкции и отправили 60M-юикс в порты выполнения. (or с источником памяти отправляется в ALU для or и порт загрузки для нагрузки, независимо от того, был ли он микроконфигурирован или нет в остальной части конвейера. nop не отправляет к порту выполнения вообще.) Аналогично, обе версии удаляют 100M недопустимых доменов, потому что здесь 40M nops.

Разница заключается в счетчиках для плавного домена.

  • Версия адреса 1-регистра только выдает и удаляет удаленные модули 80M с плавным доменом. Это то же самое, что и количество инструкций. Каждый insn превращается в один uop с плавным доменом.
  • В версии с 2-регистровыми адресами выдается 100-мегагерцовый домен. Это то же самое, что и число неудовлетворительных доменов, что указывает на то, что микро-слияние не произошло.

Я подозреваю, что вы видели только разницу между UOPS_ISSUED и UOPS_RETIRED (использовались слоты для выхода на пенсию), если неверные предсказания отрасли привели к отмене uops после выпуска, но до выхода на пенсию.

И, наконец, влияние производительности действительно. Неконсолированная версия занимала в 1,5 раза больше тактовых циклов. Это преувеличивает разницу в производительности по сравнению с большинством реальных случаев. Цикл должен выполняться в течение целого числа циклов, а два дополнительных ввода - от 2 до 3. Часто дополнительные 2-х платных доменов будут меньше. И потенциально никакой разницы, если код вставлен в бутылку чем-то другим, кроме 4-fused-domain-uops-per-cycle.

Тем не менее, код, который делает много ссылок на память в цикле, может быть более быстрым, если реализован с умеренным объемом разворачивания и увеличения количества указателей, которые используются с простой адресацией [base + immediate offset], вместо использования режимов адресации [base + index].

дальнейший материал

RIP-относительный с немедленным не может микро-предохранитель. Тест Agner Fog показывает, что это происходит даже в декодерах/uop-кешках, поэтому они никогда не сливаются в первую очередь (а не без ламинирования).

IACA ошибается и утверждает, что оба этих микроплавких предохранителя:

cmp dword  [abs mydata], 0x1b   ; fused counters != unfused counters (micro-fusion happened, and wasn't un-laminated).  Uses 2 entries in the uop-cache, according to Agner Fog testing
cmp dword  [rel mydata], 0x1b   ; fused counters ~= unfused counters (micro-fusion didn't happen)

RIP-rel делает микро-предохранитель (и остается плавленным), когда нет немедленного, например:

or  eax, dword  [rel mydata]    ; fused counters != unfused counters, i.e. micro-fusion happens

Микро-фьюжн не увеличивает латентность команды. Нагрузка может выдаваться до того, как будет готов другой вход.

ALIGN 32
.dep_fuse:
    or eax, [rsi + 0]
    or eax, [rsi + 0]
    or eax, [rsi + 0]
    or eax, [rsi + 0]
    or eax, [rsi + 0]
    dec ecx
    jg .dep_fuse

Этот цикл работает с 5 циклами на итерацию из-за цепочки eax dep. Не быстрее, чем последовательность or eax, [rsi + 0 + rdi] или mov ebx, [rsi + 0 + rdi] / or eax, ebx. (В неработающих версиях и в версиях mov выполняется одинаковое количество uops.) Проверка расписания/удаления происходит в незанятом домене. Недавно выпущенные uops отправляются в планировщик (aka Reservation Station (RS)), а также ROB. Они оставляют планировщик после отправки (он также отправляется в исполнительный блок), но остаются в ROB до выхода на пенсию. Таким образом, окно отсутствия порядка для скрытия задержки загрузки составляет, по меньшей мере, размер планировщика (54 непроверенных доменов в Sandybridge, 60 в Хасуэле, 97 в Скайлаке).

Micro-fusion не имеет ярлыка для базы и смещения, являющегося одним и тем же регистром. Цикл с or eax, [mydata + rdi+4*rdi] (где rdi обнуляется) выполняет столько циклов и циклов, сколько цикл с or eax, [rsi+rdi]. Этот режим адресации можно использовать для итерации по массиву структур с нечетным размером, начиная с фиксированного адреса. Это, вероятно, никогда не используется в большинстве программ, поэтому неудивительно, что Intel не проводила транзисторы, позволяя этому специальному корпусу режимов с двумя регистрами использовать микро-предохранитель. (И Intel в любом случае документирует его как "индексированные режимы адресации", где необходим регистр и масштабный коэффициент.)


Макрослияние cmp/jcc или dec/jcc создает uop, который остается как один uop даже в нераспространенном домене. dec / nop / jge может работать только в одном цикле, но вместо трех.

Ответ 2

Теперь я рассмотрел результаты тестов для Intel Sandy Bridge, Ivy Bridge, Haswell и Broadwell. У меня еще не было доступа к тесту на Skylake. Результаты:

  • Инструкции с двухрежимной адресацией и тремя входными зависимостями сплавляются полностью. Они берут только одну запись в кэш микроопераций, если они содержат не более 32 бит данных (или 2 * 16 бит).
  • Можно выполнить команды с четырьмя входными зависимостями, используя плавные инструкции умножения и добавления на Haswell и Broadwell. Эти инструкции все еще сливаются в один микрооператор и берут только одну запись в кэш микроопераций.
  • Инструкции с более чем 32 битами данных, например 32-разрядным адресом и 8-битными мгновенными данными, все еще могут сработать, но использовать две записи в кэше микроопераций (если только 32 бита не могут быть сжаты в 16-разрядную подписанную целое число)
  • Инструкции с rip-относительной адресацией и непосредственной константой не сливаются, даже если смещение и немедленная константа очень малы.
  • Все результаты идентичны для четырех тестируемых машин.
  • Тестирование проводилось с помощью собственных тестовых программ с использованием счетчиков контроля производительности на петлях, которые были достаточно малы, чтобы вписаться в кеш микропроцесса.

Ваши результаты могут быть вызваны другими факторами. Я не пытался использовать IACA.

Ответ 3

Мое тестирование указывает на то, что в Skylake не менее 1 процессор полностью сливает даже сложные режимы адресации, в отличие от Sandybridge.

Таким образом, версии 1-arg и 2-arg кода, вышеперечисленных Питером, выполняются в том же количестве циклов с одинаковым количеством uops отправлен и удален.

Мои результаты:

Статистика счетчиков производительности для ./uop-test:

     23.718772      task-clock (msec)         #    0.973 CPUs utilized          
    20,642,233      cycles                    #    0.870 GHz                    
    80,111,957      instructions              #    3.88  insns per cycle        
    60,253,831      uops_executed_thread      # 2540.344 M/sec                  
    80,295,685      uops_issued_any           # 3385.322 M/sec                  
    80,176,940      uops_retired_retire_slots # 3380.316 M/sec                  

   0.024376698 seconds time elapsed

Статистика счетчиков производительности для ./uop-test x:

     13.532440      task-clock (msec)         #    0.967 CPUs utilized          
    21,592,044      cycles                    #    1.596 GHz                    
    80,073,676      instructions              #    3.71  insns per cycle        
    60,144,749      uops_executed_thread      # 4444.487 M/sec                  
    80,162,360      uops_issued_any           # 5923.718 M/sec                  
    80,104,978      uops_retired_retire_slots # 5919.478 M/sec                  

   0.013997088 seconds time elapsed

Статистика счетчиков производительности для ./uop-test x x:

     16.672198      task-clock (msec)         #    0.981 CPUs utilized          
    27,056,453      cycles                    #    1.623 GHz                    
    80,083,140      instructions              #    2.96  insns per cycle        
    60,164,049      uops_executed_thread      # 3608.645 M/sec                  
   100,187,390      uops_issued_any           # 6009.249 M/sec                  
   100,118,409      uops_retired_retire_slots # 6005.112 M/sec                  

   0.016997874 seconds time elapsed

Я не нашел ни одной инструкции UOPS_RETIRED_ANY на Skylake, только парень "отставных слотов", который, по-видимому, был объединенным доменом.

Окончательный тест (uop-test x x) - это вариант, предложенный Петром, который использует RIP-относительный cmp с немедленным, который, как известно, не микрофьюзит:

.loop_riprel
    cmp dword [rel mydata], 1
    cmp dword [rel mydata], 2
    dec ecx
    nop
    nop
    nop
    nop
    jg .loop_riprel

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

Дополнительные тесты на других архитектурах приветствуются! Вы можете найти код (скопированный с Питера выше) в github.


[1]... и, возможно, некоторые другие архитектуры между Skylake и Sandybridge, поскольку Питер тестировал только SB, и я тестировал только SKL.

Ответ 4

Старые процессоры Intel без кэша uop могут выполнять слияние, поэтому, возможно, это недостаток кэша uop. У меня нет времени, чтобы протестировать это прямо сейчас, но я добавлю тест для uop fusion в следующий раз, когда обновляю скрипты . Вы пробовали с инструкциями FMA? Они являются единственными инструкциями, которые допускают 3 входных зависимостей в неиспользуемом uop.