此循环在Intel Conroe / Merom上每3个循环运行一次,按预期在imul
吞吐量上出现瓶颈。但是在Haswell / Skylake上,它每11个循环运行一次,显然是因为setnz al
依赖于最后imul
。
; synthetic micro-benchmark to test partial-register renaming
mov ecx, 1000000000
.loop: ; do{
imul eax, eax ; a dep chain with high latency but also high throughput
imul eax, eax
imul eax, eax
dec ecx ; set ZF, independent of old ZF. (Use sub ecx,1 on Silvermont/KNL or P4)
setnz al ; ****** Does this depend on RAX as well as ZF?
movzx eax, al
jnz .loop ; }while(ecx);
如果setnz al
取决于rax
,则3ximul / setcc / movzx序列形成循环携带的依赖链。如果不是,则每个setcc
/ movzx
/ 3x imul
链都是独立的,与更新循环计数器的dec
分开。在HSW / SKL上测量的每次迭代11c完全由延迟瓶颈解释:3x3c(imul)+ 1c(由setcc读取 - 修改 - 写入)+ 1c(同一寄存器中的movzx)。
偏离主题:避免这些(故意)瓶颈
我采用可理解/可预测的行为来隔离部分注册内容,而不是最佳性能。
例如,xor
- 零/设置标记/ setcc
无论如何都更好(在这种情况下,xor eax,eax
/ dec ecx
/ setnz al
)。这打破了所有CPU上的eax(除了像PII和PIII这样的早期P6系列),仍然避免了部分寄存器合并处罚,并节省了1c的movzx
延迟。它还在handle xor-zeroing in the register-rename stage的CPU上使用少一个ALU uop。有关使用setcc
进行xor-zeroing的更多信息,请参阅该链接。
请注意,AMD,Intel Silvermont / KNL和P4根本不进行部分注册重命名。它只是英特尔P6系列CPU及其后代英特尔Sandybridge系列中的一项功能,但似乎已逐步淘汰。
不幸的是,gcc确实倾向于使用cmp
/ setcc al
/ movzx eax,al
,它可以使用xor
代替movzx
(Godbolt compiler-explorer example),而clang使用xor-zero / cmp / setcc,除非你结合了多个布尔条件,如count += (a==b) | (a==~b)
。
xor / dec / setnz版本在Skylake,Haswell和Core2上每次迭代运行3.0c(在imul
吞吐量上遇到瓶颈)。 xor
- 归零可以打破除PPro / PII / PIII /早期Pentium-M之外的所有无序CPU对eax
的旧值的依赖性(它仍然避免部分寄存器合并)处罚,但没有打破dep)。 Agner Fog's microarch guide describes this。当mov eax,0
在imul
后读取eax
时,用setnz al
替换xor-zeroing会使其降低到Core2上每4.78个周期一个2-3c stall (in the front-end?) to insert a partial-reg merging uop。
另外,我使用了movzx eax, al
来消除mov-elimination,就像mov rax,rax
一样。 (IvB,HSW和SKL可以用0延迟重命名movzx eax, bl
,但Core2不能。除了部分寄存器行为外,这使得Core2 / SKL上的所有内容都相同。
Core2行为与Agner Fog's microarch guide一致,但HSW / SKL行为并非如此。从第11.10节到Skylake,以及以前的英特尔搜索相同:
通用寄存器的不同部分可以存储在不同的临时寄存器中,以消除错误依赖。
遗憾的是,他没有时间对每个新的uarch进行详细测试以重新测试假设,因此行为的这种变化从裂缝中滑落。
Agner确实描述了通过Skylake对Sandybridge上的high8寄存器(AH / BH / CH / DH)以及SnB上的low8 / low16插入(不停止)合并uop。 (不幸的是,我过去一直散布错误的信息,并说Haswell可以免费合并AH。我过快地浏览了Agner的Haswell部分,并没有注意到后面的段落关于high8如果你在其他帖子上看到我的错误评论,请告诉我。所以我可以删除它们或添加更正。我会尝试至少找到并编辑我的答案,我已经说过了。)
我的实际问题:
从IvyBridge到Skylake的一切都是一样的,包括高8的额外延迟? Intel's optimization manual并不具体说明哪些CPU具有错误的依赖关系(虽然它确实提到某些CPU具有它们),并且省略了诸如读取AH / BH / CH / DH(high8寄存器)之类的内容延迟,即使他们没有被修改。 如果Agner Fog的微型指南没有描述任何P6系列(Core2 / Nehalem)行为,那也会很有趣,但我应该限制这个范围。问问Skylake或Sandybridge家庭。 我的Skylake测试数据,将 除非另有说明,否则每条指令都使用ALU执行端口作为1个融合域uop运行。 (用 每个周期" 4"案例是对无限展开案例的推断。循环开销占用了一些前端带宽,但是每个周期优于1的任何东西都表明寄存器重命名避免了write-after-write output dependency,并且uop在内部不作为读取修改处理-write。 仅写入AH :阻止循环从环回缓冲区(又称循环流检测器(LSD))执行。 重复 为什么用通常使用ALU执行单元的指令编写 重复 术语:所有这些都离开AH(或DH)" 脏",即需要合并(使用合并的uop)时读取寄存器的其余部分(或在其他情况下)。即如果我正确理解这一点,那么AH将与RAX分开重命名。 "的清洁"恰恰相反。有很多方法可以清理脏寄存器,最简单的方法是 仅写入AL :这些循环确实从LSD运行: 我认为对低8注册的写入表现为完整注册表中的RMW混合,就像 每次迭代插入合并uop的循环都不能从LSD(循环缓冲区)运行? 我不认为AL / AH / RAX与B *,C *,DL / DH / RDX有什么特别之处。我已经在其他寄存器中对部分注册表进行了测试(即使我主要显示 我们怎样才能用一个合理的模型来解释所有这些观察结果?微观组织如何在内部工作? 相关:部分标记问题与部分注册问题不同。有关 另请参阅Problems with ADC/SBB and INC/DEC in tight loops on some CPUs了解%rep 4
短序列置于运行100M或1G迭代的小dec ebp/jnz
循环内。我使用与in my answer here相同的方式在同一硬件(桌面Skylake i7 6700k)上测量Linux perf
的周期。ocperf.py stat -e ...,uops_issued.any,uops_executed.thread
测量)。这检测到(没有)mov-elimination和额外的合并uops。lsd.uops
的计数在HSW上正好为0,在SKL上的计数很小(约为1.8k),并且不会随着循环迭代计数而缩放。可能这些计数来自某些内核代码。当循环从LSD运行时,lsd.uops ~= uops_issued
到测量噪声范围内。一些循环在LSD或no-LSD之间交替(例如,如果解码在错误的地方开始,它们可能不适合uop缓存),但在测试时我没有遇到过这种情况。
mov ah, bh
和/或mov ah, bl
每周期运行4次。它需要一个ALU uop,因此它不像mov eax, ebx
那样被消除。mov ah, [rsi]
每周期运行2次(负载吞吐量瓶颈)。mov ah, 123
每周期运行1次。 (循环内的dep-breaking xor eax,eax
消除了瓶颈。)setz ah
或setc ah
每个周期运行1次。 (一个破坏性xor eax,eax
让它成为setcc
和循环分支的p06吞吐量的瓶颈。)ah
对旧值有错误的依赖,而mov r8, r/m8
没有(对于reg或内存) src)?(那么mov r/m8, r8
呢?当然,用于reg-reg移动的两个操作码中的哪一个并不重要?)add ah, 123
每个周期运行1次,如预期的那样。add dh, cl
每周期运行1次。add dh, dh
每周期运行1次。add dh, ch
每循环运行0.5次。阅读[ABCD] H是特别的,当他们"清洁" (在这种情况下,RCX最近没有被修改过。)inc eax
或mov eax, esi
。uops_issue.any
〜= lsd.uops
。
mov al, bl
每周期运行1次。每个组偶尔会发生一次破坏性xor eax,eax
,这使得OOO执行瓶颈的uop吞吐量,而不是延迟。mov al, [rsi]
每周期运行1次,作为微融合ALU +加载uop。 (uops_issued = 4G +循环开销,uops_executed = 8G +循环开销)。
在一组4之前,一个破解xor eax,eax
会让它在每个时钟的2个负载上出现瓶颈。mov al, 123
每周期运行1次。mov al, bh
每循环运行0.5次。 (每2个循环1个)。阅读[ABCD] H很特别。xor eax,eax
+ 6x mov al,bh
+ dec ebp/jnz
:每人2c,前端每个时钟4个uop的瓶颈。add dl, ch
每循环运行0.5次。 (每2个循环1个)。阅读[ABCD] H显然会为dl
创造额外的延迟。add dl, cl
每周期运行1次。add eax, 123
一样,但如果ah
很脏,它就不会触发合并。所以(除了忽略AH
合并),它的行为与完全不进行部分注册重命名的CPU的行为相同。似乎AL
永远不会与RAX
分开重命名?
inc al
/ inc ah
对可以并行运行。mov ecx, eax
为"脏",则ah
会插入合并的uop,但实际的mov
会重命名。这是IvyBridge及其后的Agner Fog describes。movzx eax, ah
每2个循环运行一次。 (在写完整个寄存器后读取高8位寄存器会产生额外的延迟。)movzx ecx, al
没有延迟,也没有在HSW和SKL上执行执行端口。 (就像Agner Fog为IvyBridge所描述的那样,但他说HSW并没有重命名为movzx)。movzx ecx, cl
具有1c延迟并占用执行端口。 (mov-elimination never works for the same,same
case,仅在不同的架构寄存器之间。)AL
/ AH
的一致性),并且从未注意到任何差异。shr r32,cl
(甚至是{2}在Core2 / Nehalem上的shr r32,2
的一些非常奇怪的内容,请参阅INC instruction vs ADD 1: Does it matter?:不要读取除1之外的移位标记。adc
循环中的部分标记内容。
答案 0 :(得分:18)
其他答案欢迎更详细地介绍Sandybridge和IvyBridge。 我无法访问该硬件。
我还没有发现HSW和SKL之间存在任何部分注册行为差异。 在Haswell和Skylake上,到目前为止我测试的所有内容都支持这个模型:
AL永远不会与RAX分开重命名(或r15中的r15b)。因此,如果您从未触摸过high8寄存器(AH / BH / CH / DH),则所有操作都与没有部分注册重命名的CPU(例如AMD)完全相同。
对AL的只写访问权限合并到RAX中,并依赖于RAX。对于AL的加载,这是一个微融合的ALU +加载uop,它在p0156上执行,这是它在每次写入时真正合并的最有力证据之一,而不仅仅是做一些花哨的双重记录正如阿格纳所推测的那样。
Agner(和英特尔)表示,Sandybridge可能需要为AL合并uop,因此它可能会与RAX分开重命名。对于SnB,Intel's optimization manual (section 3.5.2.4 Partial Register Stalls)说
SnB(以后不一定是uarches)在以下情况下插入合并的uop:
写入其中一个寄存器AH,BH,CH或DH之后和之后 在读取同一寄存器的2-,4-或8字节形式之后。在 这些情况下插入了合并微操作。 插入消耗a 完整的分配周期,其中不能分配其他微观操作。
在目标寄存器为1或2字节的微操作之后,即 不是指令的来源(或寄存器的更大形式),和 在下面读取相同的2-,4-或8字节形式之前 寄存器。在这些情况下合并微操作是流程的一部分。
我认为他们说在SnB上,add al,bl
将RMW完整的RAX而不是单独重命名,因为其中一个源寄存器是(部分)RAX。我的猜测是,这并不适用于像mov al, [rbx + rax]
这样的负载;寻址模式下的rax
可能不会被视为来源。
我还没有测试过high8合并uops是否仍然需要在HSW / SKL上自行发布/重命名。这将使前端影响相当于4 uops(因为那是问题/重命名管道宽度)。
xor al,al
没有帮助,mov al, 0
也没有。movzx ebx, al
有zero latency (renamed),并且不需要执行单元。(即mov-elimination适用于HSW和SKL)。 它会触发AH的合并,如果它是脏的,我认为这是没有ALU工作所必需的。英特尔在引入mov-elimination的同一个uarch中降低了8的重命名,这可能不是巧合。 (Agner Fog的微型导游在这里有一个错误,他说在HSW或SKL上没有消除零扩展动作,只有IvB。)movzx eax, al
未在重命名时被删除。英特尔的mov-elimination永远不会同样适用。 mov rax,rax
也未被淘汰,即使它不必对任何事情进行零延伸。 (虽然没有必要给它特殊的硬件支持,因为它只是一个无操作,不像mov eax,eax
)。无论如何,在零扩展时,更喜欢在两个独立的架构寄存器之间移动,无论它是32位mov
还是8位movzx
。movzx eax, bx
未被删除。它具有1c延迟并使用ALU uop。英特尔的优化手册仅提到了8位movzx的零延迟(并指出永远不会重命名movzx r32, high8
)。ah
或mov ah, r8
的{{1}}的只读访问权限重命名AH,不依赖于旧值。这些都是通常不需要ALU uop的指令(对于32位版本)。mov ah, [mem]
)弄脏了它。 inc ah
取决于旧setcc ah
,但仍然会弄脏它。我认为ah
是相同的,但没有经过多次转角测试。
(原因不明:涉及mov ah, imm8
的循环有时可以从LSD运行,请参阅本文末尾的setcc ah
循环。也许只要rcr
清除<循环的em> end ,它可以使用LSD吗?)。
如果ah
变脏,ah
会合并到重命名的setcc ah
,而不是强制合并到ah
。例如rax
(%rep 4
/ inc al
/ test ebx,ebx
/ setcc ah
/ inc al
)不会生成合并的uops,只会在大约8.7c内运行(延迟8 inc ah
因inc al
的uops资源冲突而减慢。ah
/ inc ah
dep链。)
我认为这里发生的事情是setcc ah
总是被实现为读 - 修改 - 写。英特尔可能认为使用只写setcc r8
uop来优化setcc
情况并不值得,因为编译器生成的代码很少见setcc ah
}。 (但请参阅问题中的godbolt链接:clang4.0与setcc ah
会这样做。)
读取AX,EAX或RAX会触发合并uop(占用前端问题/重命名带宽)。可能RAT(寄存器分配表)跟踪架构R [ABCD] X的高8脏状态,甚至在写入AH退出之后,AH数据也存储在与RAX不同的物理寄存器中。即使在编写AH和读取EAX之间有256个NOP,也有一个额外的合并uop。 (SKL上的ROB大小= 224,因此可以保证-m32
已退役)。使用uops_issued /执行的perf计数器检测到,这清楚地显示了差异。
AL的读 - 修改 - 写(例如mov ah, 123
)免费合并,作为ALU uop的一部分。 (仅使用一些简单的uops进行测试,例如inc al
/ add
,而不是inc
或div r8
。同样,即使AH很脏,也不会触发合并的uop。
只写EAX / RAX(如mul r8
或xor eax,eax
)会清除AH-dirty状态(不合并uop)。
lea eax, [rsi + rcx]
)会首先触发AH的合并。我想这不是特殊套管,而是像任何其他RMW AX / RAX一样运行。 (TODO:测试mov ax, 1
,虽然这不应该是特殊的,因为它没有重命名。)mov ax, bx
有1c延迟,没有删除,仍然需要执行端口。xor ah,ah
/ add ah, cl
每个时钟可以运行1次(增加延迟时出现瓶颈)。使AH变脏可防止循环从LSD (循环缓冲区)运行,即使没有合并的uop。 LSD是指CPU在队列中循环uops以提供问题/重命名阶段。 (称为IDQ)。
插入合并的uops有点像为堆栈引擎插入堆栈同步uops。英特尔的优化手册说,SnB的LSD无法运行具有不匹配add al, dl
/ push
的循环,这是有道理的,但这意味着它可以< / em>使用均衡pop
/ push
运行循环。这不是我在SKL上看到的:即使是平衡pop
/ push
也阻止了从LSD运行(例如pop
/ push rax
/ {{ 1}}。(SnB&amp; LSD和HSW / SKL之间可能存在真正的差异:SnB may just "lock down" the uops in the IDQ instead of repeating them multiple times, so a 5-uop loop takes 2 cycles to issue instead of 1.25。)无论如何,看来HSW / SKL在高频时不能使用LSD。 8寄存器是脏的,或者它包含堆栈引擎uops。
此行为可能与an erratum in SKL:
有关SKL150: Short Loops Which Use AH/BH/CH/DH Registers May Cause Unpredictable System Behaviour
问题:在复杂的微架构条件下,使用AH,BH,CH或DH寄存器以及相应的较宽寄存器(例如AH的RAX,EAX或AX)的小于64指令的短循环可能导致不可预测的系统行为。只有当同一物理处理器上的两个逻辑处理器都处于活动状态时,才会发生这种情况。
这也可能与英特尔的优化手册声明有关,即SnB至少必须在一个循环中自行发布/重命名AH合并uop。这对于前端而言是一个奇怪的区别。
我的Linux内核日志显示pop rdx
。
Arch Linux的times 6 imul rax, rdx
包只提供更新you have to edit config files to actually have it loaded。所以我的Skylake测试是在i7-6700k上,微码修订版0x84,doesn't include the fix for SKL150 。在我测试的每一个案例中,它都符合Haswell的行为,IIRC。 (例如,Haswell和我的SKL都可以从LSD运行microcode: sig=0x506e3, pf=0x2, revision=0x84
/ intel-ucode
/ setne ah
/ add ah,ah
循环。我启用了HT(这是SKL150显示的前提条件),但是我在一个大多数空闲的系统上进行测试,所以我的线程有自己的核心。
使用更新的微码,LSD完全禁用所有时间,而不仅仅是部分寄存器处于活动状态。 rcr ebx,1
总是正好为零,包括真正的程序而不是合成循环。硬件错误(而不是微码错误)通常需要禁用整个功能来修复。这就是为什么SKL-avx512(SKX)是reported to not have a loopback buffer。幸运的是,这不是性能问题:SKL在Broadwell上的uop-cache吞吐量增加几乎总能跟上问题/重命名。
mov eax,ebx
从输入BL到输出BL的延迟为2c,因此即使RAX和AH不属于它,它也会增加关键路径的延迟。 (我之前已经看到了另一个操作数的这种额外延迟,在Skylake上有矢量延迟,其中int / float延迟&#34;永远污染&#34;寄存器.TODO:写出来。)< / LI>
这意味着使用lsd.uops
/ add bl, ah
解包字节与movzx ecx, al
/ movzx edx, ah
/ movzx
相比有额外的延迟,但仍然有更好的吞吐量。
当 脏时读取AH并不会增加任何延迟。 (shr eax,8
或movzx
/ add ah,ah
每次添加有1c延迟)。在许多角落里,我还没有做过很多测试来证实这一点。
假设:脏的high8值存储在物理寄存器的底部。读取干净的高电平8需要移位来提取位[15:8],但读取脏的高电平8只能取物理寄存器的位[7:0],就像正常的8位寄存器读取一样。
额外延迟并不意味着吞吐量降低。即使所有add ah,dh
指令都有2c延迟(来自读取DH,未经修改),该程序每2个时钟运行1个itier。
add dh,ah
add
一些有趣的测试循环体:
global _start
_start:
mov ebp, 100000000
.loop:
add ah, dh
add bh, dh
add ch, dh
add al, dh
add bl, dh
add cl, dh
add dl, dh
dec ebp
jnz .loop
xor edi,edi
mov eax,231 ; __NR_exit_group from /usr/include/asm/unistd_64.h
syscall ; sys_exit_group(0)
setcc版本( Performance counter stats for './testloop':
48.943652 task-clock (msec) # 0.997 CPUs utilized
1 context-switches # 0.020 K/sec
0 cpu-migrations # 0.000 K/sec
3 page-faults # 0.061 K/sec
200,314,806 cycles # 4.093 GHz
100,024,930 branches # 2043.675 M/sec
900,136,527 instructions # 4.49 insn per cycle
800,219,617 uops_issued_any # 16349.814 M/sec
800,219,014 uops_executed_thread # 16349.802 M/sec
1,903 lsd_uops # 0.039 M/sec
0.049107358 seconds time elapsed
)具有20c循环延迟,即使它有%if 1
imul eax,eax
mov dh, al
inc dh
inc dh
inc dh
; add al, dl
mov cl,dl
movzx eax,cl
%endif
Runs at ~2.35c per iteration on both HSW and SKL. reading `dl` has no dep on the `inc dh` result. But using `movzx eax, dl` instead of `mov cl,dl` / `movzx eax,cl` causes a partial-register merge, and creates a loop-carried dep chain. (8c per iteration).
%if 1
imul eax, eax
imul eax, eax
imul eax, eax
imul eax, eax
imul eax, eax ; off the critical path unless there's a false dep
%if 1
test ebx, ebx ; independent of the imul results
;mov ah, 123 ; dependent on RAX
;mov eax,0 ; breaks the RAX dependency
setz ah ; dependent on RAX
%else
mov ah, bl ; dep-breaking
%endif
add ah, ah
;; ;inc eax
; sbb eax,eax
rcr ebx, 1 ; dep on add ah,ah via CF
mov eax,ebx ; clear AH-dirty
;; mov [rdi], ah
;; movzx eax, byte [rdi] ; clear AH-dirty, and remove dep on old value of RAX
;; add ebx, eax ; make the dep chain through AH loop-carried
%endif
和%if 1
,也会从LSD运行。
setcc ah
原因不明:它从LSD运行,即使它使AH变脏。 (至少我认为是这样.TODO:尝试在add ah,ah
清除之前添加一些与00000000004000e0 <_start.loop>:
4000e0: 0f af c0 imul eax,eax
4000e3: 0f af c0 imul eax,eax
4000e6: 0f af c0 imul eax,eax
4000e9: 0f af c0 imul eax,eax
4000ec: 0f af c0 imul eax,eax
4000ef: 85 db test ebx,ebx
4000f1: 0f 94 d4 sete ah
4000f4: 00 e4 add ah,ah
4000f6: d1 db rcr ebx,1
4000f8: 89 d8 mov eax,ebx
4000fa: ff cd dec ebp
4000fc: 75 e2 jne 4000e0 <_start.loop>
Performance counter stats for './testloop' (4 runs):
4565.851575 task-clock (msec) # 1.000 CPUs utilized ( +- 0.08% )
4 context-switches # 0.001 K/sec ( +- 5.88% )
0 cpu-migrations # 0.000 K/sec
3 page-faults # 0.001 K/sec
20,007,739,240 cycles # 4.382 GHz ( +- 0.00% )
1,001,181,788 branches # 219.276 M/sec ( +- 0.00% )
12,006,455,028 instructions # 0.60 insn per cycle ( +- 0.00% )
13,009,415,501 uops_issued_any # 2849.286 M/sec ( +- 0.00% )
12,009,592,328 uops_executed_thread # 2630.307 M/sec ( +- 0.00% )
13,055,852,774 lsd_uops # 2859.456 M/sec ( +- 0.29% )
4.565914158 seconds time elapsed ( +- 0.08% )
做某事的说明。)
但是对于eax
,它在HSW / SKL上每次迭代运行5.0c(mov eax,ebx
吞吐量瓶颈)。 (已注释掉的商店/重新加载也有效,但SKL的存储转发速度比HSW快,而且它variable-latency ...)
mov ah, bl
请注意,它不再从LSD运行。
答案 1 :(得分:0)
更新:可能的证据表明,IvyBridge仍与完整的寄存器分开重命名了low16 / low8寄存器,例如Sandybridge,但不同于Haswell及其以后的版本。
InstLatX64的SnB和IvB结果显示movsx r16, r8
的吞吐量为0.33c(正如所料,movsx
从未被淘汰,在Haswell之前只有3个ALU)。
但显然InstLat的movsx r16, r8
测试瓶颈在1c吞吐率下限制了Haswell / Broadwell / Skylake(另请参阅this bug report on the instlat github)。可能是通过编写相同的体系结构寄存器来创建合并链。
(在我的Skylake上,使用单独的目标寄存器的该指令的实际吞吐量为0.25c。使用7条movsx
指令写入eax..edi和r10w / r11w进行了测试,所有指令均从cl
中读取。还有一个dec ebp/jnz
作为循环分支以进行偶数8 uop循环。)
如果我猜对了在IvB之后 IvB在CPU上产生1c吞吐量结果的原因,它的工作类似于运行movsx dx, al
块。而且,在将dx
与RDX分开重命名而不合并的CPU上,只能在超过1个IPC上运行。因此,我们可以得出结论,IvB实际上仍然确实将low8 / low16寄存器与完整寄存器分开重命名,直到Haswell放弃了。 (但是这里有些可疑:如果这个解释是正确的,我们应该在AMD上看到相同的1c吞吐量,它不会重命名部分寄存器。但是我们没有看到,请参阅下文。)
movsx r16, r8
(和movzx r16, r8
)测试的吞吐量约为0.33c:
对于0.58c
,Haswell的结果具有令人难以置信的movsx/zx r16, r8
吞吐量:
其他早晚的Haswell(和CrystalWell)/ Broadwell / Skylake结果在这两个测试中均为1.0c。
正如我在github上的InstLat链接问题中所报道的那样,movzx r32, r8
的“等待时间”数字忽略了消除运动,大概是像movzx eax, al
一样进行了测试。
更糟糕的是,具有单独寄存器测试版本的较新版本的InstLatX64(例如MOVSX r1_32, r2_8
)显示的延迟数低于1个周期,例如Skylake上的MOV SX 的延迟值为0.3c 。这完全是胡说八道;我测试只是为了确定。
MOVSX r1_16, r2_8
测试的确显示了1c的延迟,因此显然它们只是测量输出(错误)依赖项的延迟。 (对于32位及更宽的输出不存在)。
但是该MOVSX r1_16, r2_8
测试也测量了1c延迟on Sandybridge!因此,也许我的理论对movsx r16, r8
测试告诉我们的观点是错误的。>
On Ryzen (AIDA64版本4.3.781.0,2018年2月21日),我们完全不进行任何部分寄存器重命名,结果如果测试确实在重复写入相同的16位寄存器,则不会显示出我们期望的1c吞吐量影响。我也没有在使用K10或Bulldozer系列的InstLatX64较旧版本的任何较旧的AMD CPU上找到它。
43 X86 :MOVSX r16, r8 L: 0.28ns= 1.0c T: 0.11ns= 0.40c
44 X86 :MOVSX r32, r8 L: 0.28ns= 1.0c T: 0.07ns= 0.25c
45 AMD64 :MOVSX r64, r8 L: 0.28ns= 1.0c T: 0.12ns= 0.43c
46 X86 :MOVSX r32, r16 L: 0.28ns= 1.0c T: 0.12ns= 0.43c
47 AMD64 :MOVSX r64, r16 L: 0.28ns= 1.0c T: 0.13ns= 0.45c
48 AMD64 :MOVSXD r64, r32 L: 0.28ns= 1.0c T: 0.13ns= 0.45c
IDK为什么所有的吞吐量都不为0.25;似乎很奇怪。这可能是0.58c Haswell吞吐量效果的版本。 MOVZX编号相同,无前缀版本的0.25吞吐量(读取R8和写入R32)。也许较大指令的获取/解码存在瓶颈?但是movsx r32, r16
与movsx r32, r8
的大小相同。
分离式reg测试显示出与英特尔相同的模式,但是只有合并的延迟为1c。 MOVZX相同。
2252 X86 :MOVSX r1_16, r2_8 L: 0.28ns= 1.0c T: 0.08ns= 0.28c
2253 X86 :MOVSX r1_32, r2_8 L: 0.07ns= 0.3c T: 0.07ns= 0.25c
2254 AMD64 :MOVSX r1_64, r2_8 L: 0.07ns= 0.3c T: 0.07ns= 0.25c
2255 X86 :MOVSX r1_32, r2_16 L: 0.07ns= 0.3c T: 0.07ns= 0.25c
挖掘机的结果也与此类似,但是产量较低。