微融合和寻址模式
使用英特尔®架构代码分析器(IACA),我发现了一些意想不到的情况(对我来说)。
以下指令使用[base+index]
寻址
addps xmm1, xmmword ptr [rsi+rax*1]
根据IACA没有微熔丝。 但是,如果我这样使用[base+offset]
addps xmm1, xmmword ptr [rsi]
IACA报告说它确实融合了。
“英特尔优化参考手册”第2-11部分提供了以下内容作为“可由所有解码器处理的微型熔合微操作”
FADD DOUBLE PTR [RDI + RSI*8]
和Agner Fog的优化组装手册也给出了使用[base+index]
寻址的微操作融合的例子。 例如,请参见第12.2节“Core2上的相同示例”。 那么正确的答案是什么?
在解码器和uop-cache中,寻址模式不会影响微融合(除了具有立即操作数的指令不能使RIP相对寻址模式微熔化)。
但是,uop和寻址模式的某些组合无法在ROB(无序内核)中保持微熔合,所以在必要时Intel SnB系列CPU“解叠”,在问题/重命名阶段。 对于问题吞吐量和无序窗口大小(ROB大小),重叠后的融合域计数是重要的。
英特尔的优化手册在第2.3.2.4节:微操作队列和循环流检测器(LSD)中描述了Sandybridge的未分层,但没有描述任何稍后的微体系结构的更改。
这些规则 ,我从SnB,HSW和SKL的实验中可以看出:
adc
和cmov
不会微熔丝。 大多数VEX编码指令也不熔断,因为它们通常有三个操作数(所以paddb xmm0, [rdi+rbx]
熔丝但vpaddb xmm0, xmm0, [rdi+rbx]
不)。 最后,第一个操作数是只写的偶尔2操作数指令,例如pabsb xmm0, [rax + rbx]
也不会融合。 IACA是错误的,应用了SnB规则。 相关:简单(非索引)寻址模式是port7(Haswell和更高版本)上专用存储地址单元可以处理的唯一寻址模式,所以它仍然可能有助于避免商店的索引寻址模式。 (一个好的方法是用单个寄存器来寻址你的dst,但用dst+(initial_src-initial_dst)
src,那么你只需要在一个循环内增加dst寄存器。)
请注意,有些指令根本不是微型熔丝(即使在解码器/ uop-cache中也是如此)。 例如shufps xmm, [mem], imm8
或vinsertf128 ymm, ymm, [mem], imm8
,通过Skylake在SnB上总是2个uops,即使它们的寄存器源版本只有1个uop。 对于带有imm8控制操作数加上通常的dest / src1,src2寄存器/内存操作数的指令,这是典型情况,但还有其他一些情况。 例如PSRLW/D/Q xmm,[mem]
(来自存储器操作数的向量移位计数)不会微熔丝,PMULLD也不会。
在阅读大量寄存器时,另请参见Agner Fog博客上关于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的行为就像adlac / cmov的Skylake。
奇怪的是HSW取消了内存源ADC和CMOV的层叠。 也许英特尔在达到运输Haswell的最后期限之前并没有考虑从SnB那里改变它。
Agner的insn表格说cmovcc r,m
和adc r,m
在HSW / SKL上完全没有微熔丝,但这与我的实验不符。 我测量的周期数与熔融域uop问题计数匹配,出现4个uops / clock问题瓶颈。 希望他会仔细检查并纠正表格。
内存 - 目标整数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
运行速度比在SKL上使用add
而不是adc
循环快。 我没有尝试使用不同的地址,因为很显然,SKL不喜欢重复地址重写(存储转发延迟高于预期)。另见这篇文章,关于重复存储/重新加载到相同地址的速度比预期慢。
内存目标adc
是如此之多的uops,因为Intel P6系列(显然是SnB系列)不能为多uop指令的所有uop保留相同的TLB条目,所以它需要一个额外的uop来解决这个问题 - 加载和添加完成,然后存储故障,但insn不能重新启动,因为CF已经更新。 Andy Glew有趣的一系列评论(@krazyglew)。
据推测,解码器中的融合和未分层可以使我们免于需要微码ROM,从adc [base+idx], reg
。的单条指令中生成超过4个融合域uops。
为什么SnB系列不层压 :
Sandybridge简化了内部uop格式以节省功耗和晶体管(同时使用物理寄存器文件进行主要更改,而不是将输入/输出数据保存在ROB中)。 SnB系列CPU只允许有限数量的输入寄存器用于无序内核中的融合域uop。 对于SnB / IvB,该限制是2个输入(包括标志)。 对于HSW及其后续版本而言,限额为3个输入。 我不确定内存目标add
和adc
是否充分利用了这一点,或者如果英特尔不得不通过一些说明让Haswell走出门外
Nehalem和更早的版本对于一个未融合域的uop有2个输入的限制,但是ROB可以明显地跟踪具有3个输入寄存器(非存储器寄存器操作数,基址和索引)的微混合微操作。
因此索引存储和ALU +加载指令仍然可以高效地进行解码(不必是组中的第一个uop),并且不会在uop缓存中占用额外的空间,否则微融合的优势基本上不适用于调优紧圈。 “未分层”发生在4-fused-domain-uops-per-cycle问题/退休宽度乱序核心之前 。 融合域性能计数器(uops_issued / uops_retired.retire_slots)在拆分后计算融合域uops。
英特尔对重命名人员的描述(第2.3.3.1节:更名)意味着这是问题/重命名阶段,实际上是进行非分层的,所以用于未分层的高级层仍然可以在28/56/64融合中进行微融合-domain uop问题队列/循环缓冲区(又名IDQ)。
TODO:测试这个。 创建一个只能适应循环缓冲区的循环。 改变一些东西,这样在发布之前,其中一个uop将被解压缩,然后看它是否仍然从循环缓冲区(LSD)运行,或者现在所有的uops是从uop缓存(DSB)重新获取的。 有perf计数器来跟踪uop从哪里来,所以这应该很容易。
更难的TODO:如果在从uop缓存读取和添加到IDQ之间发生拆分,测试它是否能减少uop-cache带宽。 或者如果在问题阶段发生分层问题,是否会影响问题吞吐量? (即它在发布第一个4后如何处理剩余的uops)
(请参阅此答案的以前版本,了解基于调整某些LUT代码的一些猜测,关于vpgatherdd
一些说明比pinsrw
循环多出约pinsrw
。)
SnB实验测试
HSW / SKL号码是在i5-4210U和i7-6700k上测量的。 两者都启用了HT(但是系统闲置,因此线程本身具有整个内核)。 我使用ocperf.py
在两个系统上运行相同的静态二进制文件,即SKL上的Linux 4.10和HSW上的Linux 4.8。 (HSW笔记本电脑NFS安装在我的SKL桌面/家中。)
如下所述,在不再工作的i5-2500k上测量SnB值。
通过使用性能计数器测试uops和周期来确认。
我找到了英特尔Sandybridge的PMU事件表,用于Linux的perf
命令。 (不幸的是,标准的perf
没有大多数特定于硬件的PMU事件的象征名,比如uops。)我利用它来得到最近的答案。
ocperf.py
为这些uarch特定的PMU事件提供了符号名称,所以您不必查找表格。 此外,同一个符号名称适用于多个用户。 当我第一次写这个答案时,我并没有意识到这一点。
为了测试uop微融合,我构建了一个测试程序,这个测试程序在英特尔CPU的每循环4次高频融合域极限上存在瓶颈。 为了避免任何执行端口争用,这些uop中的许多都是nop
,它们仍然位于uop缓存中,并通过与其他uop相同的流水线,除非它们不会被分派到执行端口。 (一个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
我还发现,如果环路不是4个uops的倍数,则循环缓冲区内的uop带宽不是每个周期4个常量。 (即它是abc
, abc
,...;不是abca
, bcab
,...)。 不幸的是,Agner Fog的微型文档在循环缓冲区的限制上并不清楚。 请参阅在执行uop数量不是处理器宽度倍数的循环时性能是否降低? 对HSW / SKL进行更多调查。 在这种情况下,SnB可能比HSW差,但我不确定,并且仍然没有工作的SnB硬件。
我想将宏观融合(比较和分支)保留在图片之外,所以我在dec
和分支之间使用了nop
。 我使用了4个nop
,所以在微融合时,循环将是8个uops,并且每次迭代以2个周期填充管道。
在其他版本的循环中,使用不进行微熔丝的2操作数寻址模式,该循环将是10个熔融域uops,并在3个循环中运行。
来自3.3GHz Intel Sandybridge(i5 2500k)的结果。 在测试之前,我没有做任何事情来让cpufreq调速器提高时钟速度,因为循环是不与内存进行交互的循环。 我已经为必须以十六进制输入的性能计数器事件添加了注释。
测试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
使用内存源为or
分派ALU,并为负载分派一个加载端口,而不管它是否在流水线的其余部分是微融合的nop
根本不派遣到执行端口)同样,两个版本都退出了100M未融合域的uops,因为这里有40M个nops。
区别在于融合域的计数器。
我怀疑你只会看到UOPS_ISSUED和UOPS_RETIRED(退役插槽使用)之间的差异,如果分支预测失误导致uops在发布后被取消,但在退休之前。
最后,性能影响是真实的。 非融合版本花费了1.5倍的时钟周期。 与大多数真实案例相比,这夸大了性能差异。 循环必须运行多个循环,并且2个额外的uops将它从2推到3.通常,额外的2个融合域uops将会减少差异。 如果代码被除了4-fused-domain-uops-per-cycle之外的其他东西所掩盖,那么可能没有区别。
尽管如此,如果通过适度的展开和递增多指针来实现循环中的大量内存引用,那么使用简单的[base + immediate offset]
寻址代替使用[base + index]
寻址模式。
进一步的东西
RIP相对立即不能微型保险丝 。 Agner Fog的测试表明,甚至在解码器/ uop-cache中也是如此,所以它们从不首先融合(而不是未叠加)。
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's 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
由于eax
dep链,此循环每次迭代运行5个周期。 不要比序列or eax, [rsi + 0 + rdi]
或mov ebx, [rsi + 0 + rdi] / or eax, ebx
。 (未融合版本和mov
版本都运行相同数量的uops。)调度/ dep检查发生在未融合域中。 新发布的微软进入调度程序(又名Reservation Station(RS))以及ROB。 他们在派遣之后离开调度员(又被送到执行部门),但留在ROB直到退休。 因此隐藏负载等待时间的无序窗口至少是调度程序的大小(Sandybridge 54个未融合域的uops,Haswell的60个,Skylake的97个)。
微融合没有一个快捷方式的基地和偏移是相同的寄存器。 带有or eax, [mydata + rdi+4*rdi]
的循环or eax, [mydata + rdi+4*rdi]
(其中rdi被清零)与循环or eax, [rsi+rdi]
一样运行尽可能多的uops和循环。 这种寻址模式可用于遍历从固定地址开始的奇数大小结构数组。 这在大多数程序中可能从未使用,因此英特尔没有花费晶体管来允许这种双寄存器模式的特殊情况进行微型熔丝就不足为奇了。 (无论如何,英特尔将其称为“索引寻址模式”,其中需要寄存器和比例因子。)
cmp
/ jcc
或dec
/ jcc
宏融合创建了一个uop,即使在未融合域中也能保持单个uop。 dec / nop / jge
仍然可以在单个循环中运行,但是是三个uops而不是一个。
注意:自从我写了这个答案以后,Peter也测试了Haswell和Skylake,并将结果集成到了上面所接受的答案中(特别是,下面大部分针对Skylake的改进似乎实际上都出现在Haswell中)。 你应该可以看到CPU之间行为流逝的答案,这个答案(尽管没有错)主要是有历史意义的。
我的测试表明,至少在Skylake上,处理器完全融合了复杂的寻址模式,这与Sandybridge不同。
也就是说,Peter上面发布的代码的1-arg和2-arg版本运行的循环次数相同,并且有相同数量的uop被派遣和退役。
我的结果:
./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 xx
:
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
我没有在Skylake上找到任何UOPS_RETIRED_ANY指令,只有那些显然是融合域的“退役老虎机”家伙。
最后的测试( uop-test xx
)是Peter建议的一种变体,它使用与RIP相关的cmp
与immediate,这种变体已知的不是microfuse:
.loop_riprel
cmp dword [rel mydata], 1
cmp dword [rel mydata], 2
dec ecx
nop
nop
nop
nop
jg .loop_riprel
结果显示,每个周期额外的2个微处理器被uops发出和退出的计数器拾取(因此测试可以区分融合发生与否)。
欢迎在其他体系结构上进行更多测试! 你可以在github中找到代码(从Peter上面复制)。
[1] ...也许在Skylake和Sandybridge之间还有其他架构,因为彼得只测试了SB,而我只测试了SKL。
我现在已经回顾了英特尔Sandy Bridge,Ivy Bridge,Haswell和Broadwell的测试结果。 我还没有通过Skylake测试。 结果是:
您的结果可能是由于其他因素。 我没有尝试过使用IACA。
链接地址: http://www.djcxy.com/p/79179.html