Режимы микросинтеза и адресации
Я обнаружил что-то неожиданное (для меня) с помощью анализатора кода архитектуры Intel® (IACA).
Следующая инструкция с использованием [base+index]
адресация
addps xmm1, xmmword ptr [rsi+rax*1]
не микроплавкий согласно IACA. Однако, если я использую [base+offset]
как это
addps xmm1, xmmword ptr [rsi]
МАКА сообщает, что это действительно взрыватель.
В разделе 2-11 справочного руководства по оптимизации Intel в качестве примера приводится следующее "микроплавких микроопераций, которые могут обрабатываться всеми декодерами"
FADD DOUBLE PTR [RDI + RSI*8]
и руководство по сборке оптимизация Agner Fog также дает примеры микрооперации с использованием [base+index]
адресации. См., Например, Раздел 12.2 "Тот же пример на Core2". Так какой правильный ответ?
4 ответа
В декодерах и uop-кеше режим адресации не влияет на микрослияние (за исключением того, что инструкция с непосредственным операндом не может микрозонить режим адресации, относящийся к RIP).
Но некоторые комбинации режима uop и адресации не могут оставаться микроплавкими в ROB (в неработающем ядре), поэтому процессоры семейства Intel SnB "разглаживают" при необходимости, в какой-то момент перед проблемой / переименовать этап. Что касается пропускной способности и размера окна не по порядку (размер ROB), то имеет значение число uop в слитых доменах после расслоения.
Руководство по оптимизации Intel описывает отсутствие ламинирования для Sandybridge в разделе 2.3.2.4: Очередь микроопераций и детектор Loop Stream Detector (LSD), но не описывает изменения для более поздних микроархитектур.
Правила, насколько я могу судить по экспериментам на SnB, HSW и SKL:
- SnB (и я полагаю, также IvB): индексированные режимы адресации всегда не ламинированы, другие остаются в микроплавком виде. IACA (в основном?) Правильно.
- HSW, SKL: они сохраняют микрозонку с индексированной инструкцией ALU только в том случае, если она имеет 2 операнда и обрабатывает регистр dst как чтение-изменение-запись. Здесь "операнды" включают флаги, означающие, что
adc
а такжеcmov
не микроплавкий Большинство VEX-кодированных инструкций также не сливаются, так как они обычно имеют три операнда (поэтомуpaddb xmm0, [rdi+rbx]
но предохранителиvpaddb xmm0, xmm0, [rdi+rbx]
не делает). Наконец, случайная инструкция с 2 операндами, где первый операнд только для записи, такой какpabsb xmm0, [rax + rbx]
также не предохранитель. IACA не прав, применяя правила SnB.
Связанный: простые (неиндексированные) режимы адресации - единственные, которые может обрабатывать выделенный модуль адреса магазина на порту 7 (Haswell и более поздние версии), поэтому все еще потенциально полезно избегать индексированных режимов адресации для хранилищ. (Хороший трюк для этого - обратиться к dst с помощью одного регистра, но src с dst+(initial_src-initial_dst)
, Тогда вам нужно только увеличить регистр dst внутри цикла.)
Обратите внимание, что некоторые инструкции вообще никогда не сливаются (даже в декодерах / uop-cache). например shufps xmm, [mem], imm8
, или же vinsertf128 ymm, ymm, [mem], imm8
, всегда 2 мопа на SnB через Skylake, даже если их версии в регистре-источнике только 1 моп. Это типично для команд с операндом управления imm8 плюс обычные операнды dest/src1, регистр / память src2, но есть несколько других случаев. например PSRLW/D/Q xmm,[mem]
(векторное смещение из операнда памяти) не имеет микросреды и не имеет значения PMULLD.
См. Также этот пост в блоге Агнера Фога для обсуждения ограничений пропускной способности для HSW/SKL при чтении большого количества регистров: большое количество микросинтезов с индексированными режимами адресации может привести к замедлению по сравнению с теми же инструкциями с меньшим количеством операндов регистров: зарегистрировать режимы адресации и сразу. Мы пока не знаем причину, но я подозреваю, что какое-то ограничение на чтение регистров, возможно, связано с чтением большого количества холодных регистров из PRF.
Контрольные примеры, цифры из реальных измерений: все эти микроплавкие предохранители в декодерах 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's 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.
Я предполагаю, что Broadwell ведет себя как Skylake для ADC / CMOV.
Странно, что HSW не ламинирует источник памяти ADC и CMOV. Возможно, Intel не смогла изменить это с SnB до того, как они уложились в срок поставки Haswell.
В таблице Агнера говорится: cmovcc r,m
а также adc r,m
на HSW/SKL вообще не сливаюсь, но это не соответствует моим экспериментам. Количество циклов, которое я измеряю, совпадает с числом проблем с мопами в слитых доменах, для узкого места проблемы 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,
Память-назначения adc
так много мопов, потому что семейство Intel P6 (и, по-видимому, семейство SnB) не может хранить одинаковые записи TLB для всех мопов многопользовательской инструкции, поэтому для обхода проблемного случая, когда загрузка и добавление завершены, а затем происходит сбой хранилища, но insn нельзя просто перезапустить, потому что CF уже обновлен. Интересная серия комментариев от Энди Глеу (@krazyglew).
Предположительно, слияние в декодерах и отсутствие ламинирования в дальнейшем избавляет нас от необходимости использования микрокода ПЗУ для производства более 4-х мопов с плавкой областью из одной инструкции для adc [base+idx], reg
,
Почему нон-ламинаты семейства SnB:
Sandybridge упростил внутренний формат UOP для экономии энергии и транзисторов (наряду с внесением основных изменений в использование файла физического регистра вместо сохранения данных ввода / вывода в ROB). Процессоры семейства SnB допускают только ограниченное количество входных регистров для uop слитых доменов в ядре с неправильным порядком. Для SnB/IvB этот предел составляет 2 входа (включая флаги). Для HSW и позже, предел составляет 3 входа для моп. Я не уверен, если память назначения add
а также adc
в полной мере воспользоваться этим, или если Intel пришлось вывести Haswell за дверь с некоторыми инструкциями
Nehalem и более ранние версии имеют ограничение в 2 входа для мопов с неиспользуемым доменом, но ROB, по-видимому, может отслеживать микроплавкие мопы с 3 входными регистрами (операнд регистра без памяти, база и индекс).
Таким образом, индексированные хранилища и инструкции загрузки ALU+ могут все еще эффективно декодироваться (не обязательно быть первым мопом в группе) и не занимать дополнительное место в кеше мопов, но в противном случае преимущества микро-слияния по существу уходят на настройку плотные петли. "un-lamination" происходит до того, как ядро 4-fused-domain-uops-per-loop выдает / выводит из строя ширину ядра. Счетчики производительности слитого домена (uops_issued / uops_retired.retire_slots) подсчитывают количество мопов слитого домена после отсутствия ламинирования.
В описании Intel по переименованию (Раздел 2.3.3.1: Renamer) подразумевается, что именно этап выпуска / переименования фактически выполняет неслоение, так что мопы, предназначенные для несламинирования, могут все еще быть микроплавлеными в сплавленном 28/56/64 -domain uop выдает очередь / loop-buffer (также известный как IDQ).
ТОДО: проверить это. Создайте цикл, который едва помещается в буфер цикла. Измените что-нибудь так, чтобы один из мопов был не ламинирован перед выдачей, и посмотрите, работает ли он из буфера цикла (LSD), или же все мопы теперь повторно выбираются из кеша мопов (DSB). Есть счетчики перфокарт, чтобы отследить, откуда происходят мопы, так что это должно быть легко.
Сложнее TODO: если происходит не ламинирование между чтением из кэша UOP и добавлением в IDQ, проверьте, может ли оно когда-либо уменьшить пропускную способность кэша UOP. Или, если расслоение происходит прямо на этапе выпуска, может ли это повлиять на пропускную способность выпуска? (т.е. как он обрабатывает оставшиеся мопы после выдачи первых 4.)
(См. Предыдущую версию этого ответа для некоторых предположений, основанных на настройке некоторого кода LUT, с некоторыми примечаниями по vpgatherdd
примерно в 1,7 раза больше циклов, чем 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, который больше не работает.
Confirmed by testing with performance counters for uops and cycles.
I found a table of PMU events for Intel Sandybridge, for use with Linux's perf
команда. (Standard perf
unfortunately doesn't have symbolic names for most hardware-specific PMU events, like uops.) I made use of it for a recent answer.
ocperf.py
provides symbolic names for these uarch-specific PMU events, so you don't have to look up tables. Also, the same symbolic name works across multiple uarches. I wasn't aware of it when I first wrote this answer.
To test for uop micro-fusion, I constructed a test program that is bottlenecked on the 4-uops-per-cycle fused-domain limit of Intel CPUs. To avoid any execution-port contention, many of these uops are nop
s, which still sit in the uop cache and go through the pipeline the same as any other uop, except they don't get dispatched to an execution port. (An xor x, same
, or an eliminated move, would be the 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
I also found that the uop bandwidth out of the loop buffer isn't a constant 4 per cycle, if the loop isn't a multiple of 4 uops. (ie it's abc
, abc
,...; не abca
, bcab
...) Agner Fog's microarch doc unfortunately wasn't clear on this limitation of the loop buffer. See /questions/47775236/snizhaetsya-li-proizvoditelnost-pri-vyipolnenii-tsiklov-chislo-operatsij-kotoryih-ne-kratno-shirine-protsessora for more investigation on HSW/SKL. SnB may be worse than HSW in this case, but I'm not sure and don't still have working SnB hardware.
I wanted to keep macro-fusion (compare-and-branch) out of the picture, so I used nop
между dec
and the branch. I used 4 nop
s, so with micro-fusion, the loop would be 8 uops, and fill the pipeline with at 2 cycles per 1 iteration.
In the other version of the loop, using 2-operand addressing modes that don't micro-fuse, the loop will be 10 fused-domain uops, and run in 3 cycles.
Results from my 3.3GHz Intel Sandybridge (i5 2500k). I didn't do anything to get the cpufreq governor to ramp up clock speed before testing, because cycles are cycles when you aren't interacting with memory. I've added annotations for the performance counter events that I had to enter in hex.
testing the 1-reg addressing mode: 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
testing the 2-reg addressing mode: with a 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
So, both versions ran 80M instructions, and dispatched 60M uops to execution ports. (or
with a memory source dispatches to an ALU for the or
, and a load port for the load, regardless of whether it was micro-fused or not in the rest of the pipeline. nop
doesn't dispatch to an execution port at all.) Similarly, both versions retire 100M unfused-domain uops, because the 40M nops count here.
The difference is in the counters for the fused-domain.
- The 1-register address version only issues and retires 80M fused-domain uops. This is the same as the number of instructions. Each insn turns into one fused-domain uop.
- The 2-register address version issues 100M fused-domain uops. This is the same as the number of unfused-domain uops, indicating that no micro-fusion happened.
I suspect that you'd only see a difference between UOPS_ISSUED and UOPS_RETIRED(retirement slots used) if branch mispredicts led to uops being cancelled after issue, but before retirement.
And finally, the performance impact is real. The non-fused version took 1.5x as many clock cycles. This exaggerates the performance difference compared to most real cases. The loop has to run in a whole number of cycles, and the 2 extra uops push it from 2 to 3. Often, an extra 2 fused-domain uops will make less difference. And potentially no difference, if the code is bottlecked by something other than 4-fused-domain-uops-per-cycle.
Still, code that makes a lot of memory references in a loop might be faster if implemented with a moderate amount of unrolling and incrementing multiple pointers which are used with simple [base + immediate offset]
addressing, instead of the using [base + index]
addressing modes.
futher stuff
RIP-relative with an immediate can't micro-fuse. Agner Fog's testing shows that this is the case even in the decoders / uop-cache, so they never fuse in the first place (rather than being un-laminated).
IACA gets this wrong, and claims that both of these micro-fuse:
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's testing
cmp dword [rel mydata], 0x1b ; fused counters ~= unfused counters (micro-fusion didn't happen)
RIP-rel does micro-fuse (and stay fused) when there's no immediate, eg:
or eax, dword [rel mydata] ; fused counters != unfused counters, i.e. micro-fusion happens
Micro-fusion doesn't increase the latency of an instruction. The load can issue before the other input is ready.
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
This loop runs at 5 cycles per iteration, because of the eax
dep chain. No faster than a sequence of or eax, [rsi + 0 + rdi]
, или же mov ebx, [rsi + 0 + rdi] / or eax, ebx
, (The unfused and the mov
versions both run the same number of uops.) Scheduling / dep checking happens in the unfused-domain. Newly issued uops go into the scheduler (aka Reservation Station (RS)) as well as the ROB. They leave the scheduler after dispatching (aka being sent to an execution unit), but stay in the ROB until retirement. So the out-of-order window for hiding load latency is at least the scheduler size ( 54 unfused-domain uops in Sandybridge, 60 in Haswell, 97 in Skylake).
Micro-fusion doesn't have a shortcut for the base and offset being the same register. A loop with or eax, [mydata + rdi+4*rdi]
(where rdi is zeroed) runs as many uops and cycles as the loop with or eax, [rsi+rdi]
, This addressing mode could be used for iterating over an array of odd-sized structs starting at a fixed address. This is probably never used in most programs, so it's no surprise that Intel didn't spend transistors on allowing this special-case of 2-register modes to micro-fuse. (And Intel documents it as "indexed addressing modes" anyway, where a register and scale factor are needed.)
Macro-fusion of a cmp
/ jcc
или же dec
/ jcc
creates a uop that stays as a single uop even in the unfused-domain. dec / nop / jge
can still run in a single cycle but is three uops instead of one.
Примечание: с тех пор как я написал этот ответ, Питер также проверил Haswell и Skylake и интегрировал результаты в принятый выше ответ (в частности, большинство улучшений, которые я приписываю Skylake ниже, похоже, действительно появилось в Haswell). Вы должны увидеть этот ответ для краткого описания поведения разных процессоров, и этот ответ (хотя и не неправильный) в основном представляет исторический интерес.
Мои тесты показывают, что на Skylake по крайней мере 1 процессор полностью объединяет даже сложные режимы адресации, в отличие от Sandybridge.
То есть версии кода с 1 и 2 аргументами, опубликованные Питером выше, выполняются с одинаковым числом циклов, с одинаковым числом отправленных и удаленных мопов.
Мои результаты:
Статистика счетчика производительности для ./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.
Старые процессоры Intel без UOP-кеша могут сделать слияние, так что, возможно, это недостаток UOP-кеша. У меня нет времени, чтобы проверить это прямо сейчас, но я добавлю тест на UOP Fusion в следующий раз, когда обновлю свои тестовые сценарии. Вы пробовали с инструкциями FMA? Это единственные инструкции, которые допускают 3 входных зависимости в неиспользованном мопе.
Теперь я рассмотрел результаты испытаний для Intel Sandy Bridge, Ivy Bridge, Haswell и Broadwell. У меня еще не было доступа к тестированию на Skylake. Результаты:
- Инструкции с двумя регистрами адресации и тремя входными зависимостями смешиваются. Они занимают только одну запись в кэше микроопераций, если они содержат не более 32 бит данных (или 2 * 16 бит).
- Можно создать инструкции с четырьмя входными зависимостями, используя объединенные инструкции умножения и добавления в Haswell и Broadwell. Эти инструкции все еще объединяются в одну микрооперацию и занимают только одну запись в кэше микрооперации.
- Инструкции с более чем 32-битными данными, например, 32-битный адрес и 8-битные непосредственные данные могут все еще сливаться, но использовать две записи в кеше микрооперации (если 32-битные не могут быть сжаты в 16-битное целое число со знаком)
- Инструкции с относительной адресацией rip и непосредственной константой не смешиваются, даже если смещение и непосредственная константа очень малы.
- Все результаты идентичны на четырех протестированных машинах.
- Тесты проводились с моими собственными тестовыми программами с использованием счетчиков контроля производительности в циклах, которые были достаточно малы, чтобы поместиться в кэш микроопераций.
Ваши результаты могут быть связаны с другими факторами. Я не пытался использовать IACA.