Performance Haswell AVX/FMA延迟测试比英特尔指南中说的慢1个周期
在《英特尔Intrinsics指南》中,vmulpd和vfmadd213pd的延迟为5,vaddpd的延迟为3 我写了一些测试代码,但是所有的结果都慢了一个周期 以下是我的测试代码:Performance Haswell AVX/FMA延迟测试比英特尔指南中说的慢1个周期,performance,x86-64,intel,cpu-architecture,avx,Performance,X86 64,Intel,Cpu Architecture,Avx,在《英特尔Intrinsics指南》中,vmulpd和vfmadd213pd的延迟为5,vaddpd的延迟为3 我写了一些测试代码,但是所有的结果都慢了一个周期 以下是我的测试代码: .CODE test_latency PROC vxorpd ymm0, ymm0, ymm0 vxorpd ymm1, ymm1, ymm1 loop_start: vmulpd ymm0, ymm0, ymm1 vmulpd ymm0, ymm0, ymm1 v
.CODE
test_latency PROC
vxorpd ymm0, ymm0, ymm0
vxorpd ymm1, ymm1, ymm1
loop_start:
vmulpd ymm0, ymm0, ymm1
vmulpd ymm0, ymm0, ymm1
vmulpd ymm0, ymm0, ymm1
vmulpd ymm0, ymm0, ymm1
sub rcx, 4
jg loop_start
ret
test_latency ENDP
END
包括
包括
包括
包括
外部空隙试验延迟64分钟;
int main
{
SetThreadAffinityMaskGetCurrentThread,1;//避免上下文切换
int64_t n=int64_t3e9;
双启动=omp\u get\u时间;
试验_latencyn;
双端=omp\u get\u时间;
双倍时间=结束-开始;
double freq=3.3e9;//我的CPU频率
双延迟=频率*时间/n;
打印平坦=%f\n,延迟;
}
我的CPU是核心i5 4590,我把它的频率锁定在3.3GHz。输出为:延迟=6.102484
奇怪的是,如果我将vmulpd ymm0,ymm0,ymm1更改为vmulpd ymm0,ymm0,ymm0,那么输出变成:latency=5.093745
有什么解释吗?我的测试代码有问题吗
更多结果
我猜
我更改了测试延迟,如下所示:
.CODE
test_latency PROC
vxorpd ymm0, ymm0, ymm0
vxorpd ymm1, ymm1, ymm1
loop_start:
vaddpd ymm1, ymm1, ymm1 ; added this line
vmulpd ymm0, ymm0, ymm1
vmulpd ymm0, ymm0, ymm1
vmulpd ymm0, ymm0, ymm1
vmulpd ymm0, ymm0, ymm1
sub rcx, 4
jg loop_start
ret
test_latency ENDP
END
最后得到了5个循环的结果。还有其他说明可以达到相同的效果:
vmovupd ymm1, ymm0
vmovupd ymm1, [mem]
vmovdqu ymm1, [mem]
vxorpd ymm1, ymm1, ymm1
vpxor ymm1, ymm1, ymm1
vmulpd ymm1, ymm1, ymm1
vshufpd ymm1, ymm1, ymm1, 0
但这些指示不能:
vmovupd ymm1, ymm2 ; suppose ymm2 is zeroed
vpaddq ymm1, ymm1, ymm1
vpmulld ymm1, ymm1, ymm1
vpand ymm1, ymm1, ymm1
对于ymm指令,我想避免1个额外循环的条件是:
所有输入都来自同一个域。
所有输入都足够新鲜。“从旧值移动”不起作用
至于VEX xmm,条件似乎有点模糊。它似乎与上半个州有关,但我不知道哪一个更干净:
vxorpd ymm1, ymm1, ymm1
vxorpd xmm1, xmm1, xmm1
vzeroupper
这对我来说是个很难回答的问题。自从在Skylake上注意到这件事以来,几年来我一直想写一些关于这件事的东西 绕过延迟延迟是有粘性的:整数SIMD指令可能会感染将来读取该值的所有指令,即使在指令执行很长时间后也是如此。我很惊讶,这种感染在归零习惯用法中幸存了下来,特别是像vxorpd这样的FP归零指令,但我可以在SKL i7-6700k上重现这种影响,在Linux上使用perf直接在测试循环中计算时钟周期,而不是在时间和频率上乱搞 在Skylake上,似乎在循环正常工作之前,一行有3条或更多vxorpd归零指令,消除了额外的旁路延迟。另外,xor归零总是被消除的,不像mov消除有时会失败。但也许区别只是在后端的vpaddb发行和第一个vmulpd发行之间产生了一个缺口;在我的测试循环中,我在循环之前弄脏/污染了寄存器 更新:现在再次尝试我的测试代码,即使是一个vxorps似乎也能清除寄存器。也许微码更新改变了什么 可能调用方先前使用的YMM1涉及整数指令。TODO:调查寄存器进入这种状态的情况有多普遍,以及它何时能够在异或归零后存活!我希望只有在使用整数指令构造FP位模式时才会发生这种情况,包括vpcmpeqd ymm1、ymm1、ymm1之类的东西,以使a-NaN都是一位 在Skylake上,我可以通过在循环之前,在xor归零之后,执行vaddpd ymm1,ymm1,ymm1来修复它。或之前;可能没关系!这可能更为理想,将其放在前一个dep链的末尾,而不是此链的开头 正如我所写 xsave/rstor可以解决使用 像padd这样的SIMD整数指令会无限期地产生额外的延迟 用于使用FP指令读取,会影响两种指令的延迟 投入。e、 g.padd xmm0,xmm0然后在一个循环中addps xmm1,xmm0有5c 延迟,而不是通常的4,直到下一次保存/恢复 它是 绕过延迟,但即使不触摸寄存器也会发生 直到padd通过使用>ROB进行填充而完全退出 循环之前的UOP 测试程序: Perf在i7-6700k上生成静态可执行文件:
Performance counter stats for './foo' (4 runs):
129.01 msec task-clock # 0.998 CPUs utilized ( +- 0.51% )
0 context-switches # 0.000 K/sec
0 cpu-migrations # 0.000 K/sec
2 page-faults # 0.016 K/sec
500,053,798 cycles # 3.876 GHz ( +- 0.00% )
50,000,042 branches # 387.576 M/sec ( +- 0.00% )
200,000,059 instructions # 0.40 insn per cycle ( +- 0.00% )
150,020,084 uops_issued.any # 1162.883 M/sec ( +- 0.00% )
150,014,866 uops_executed.thread # 1162.842 M/sec ( +- 0.00% )
0.129244 +- 0.000670 seconds time elapsed ( +- 0.52% )
50米迭代的500米循环=10个循环对2个VADDP进行依赖,或每个循环5个。我尝试在vxorpd之前或之后添加vaddpd ymm1、ymm1、ymm1,但vmulpd ymm0、ymm0、ymm1的延迟仍然是6。@kevinjwz:不幸的是,我没有一个可以测试的Haswell系统,但我可以在Skylake上重新进行测试。vpaddb ymm1,ymm1,ymm1在循环感染寄存器之前,使其变慢。vaddpd ymm1,ymm1,ymm1在这之后,使其再次快速每vmulpd 4个周期;Skylake为mul/add/FMA提供了4c延迟,放弃了Haswell拥有的3c延迟专用FP-add单元。我可以确认vxorpd在vpaddb之后的归零不会清除寄存器!!不过,FP shuffle与vunpcklpd类似。或3次或3次以上的异或归零重复。非常神秘。re:在Skylake上,似乎在循环正常工作之前,有3条或更多的vxorpd归零指令,消除了额外的旁路延迟。你用1x vxorpd+nop fill测试过它是否真的只是分离解码组吗?@Noah:没有,我还没有。你能在你的威士忌湖机器上重新设定效果吗?和/或冰
Lake?你能把基准代码发布到某个地方吗?我可以试试。你进一步的测试都表明,如果你读一个寄存器而不写它,它的额外延迟属性可以保留整个循环,通过另一个操作数影响依赖链。而且vzeroupper可以清除Haswell上的这个属性。Skylake上没有。@PeterCordes实际上vzeroupper只能更改vmulpd xmm0、xmm0、xmm1的延迟;它不会对vmulpd ymm0、ymm0、ymm1进行更改。所以我还是很好奇,很有趣。在Skylake上,vzeroupper也不修复xmm,如果只读寄存器被污染,速度仍然很慢。但是vzeroupper有着不同的实现细节,这也导致了它的不同。
; taskset -c 3 perf stat --all-user -etask-clock,context-switches,cpu-migrations,page-faults,cycles,branches,instructions,uops_issued.any,uops_executed.thread -r1 ./bypass-latency
default rel
global _start
_start:
vmovaps xmm1, [one] ; FP load into ymm1 (zeroing the upper lane)
vpaddd ymm1, ymm1,ymm0 ; ymm1 written in the ivec domain
;vxorps ymm1, ymm1,ymm1 ; In 2017, ymm1 still makes vaddps slow (5c) after this
; but I can't reproduce that now with updated microcode.
vxorps ymm0, ymm0, ymm0 ; zeroing-idiom on ymm0
mov rcx, 50000000
align 32 ; doesn't help or hurt, as expected since the bottleneck isn't frontend
.loop:
vaddps ymm0, ymm0,ymm1
vaddps ymm0, ymm0,ymm1
dec rcx
jnz .loop
xor edi,edi
mov eax,231
syscall ; exit_group(0)
section .rodata
align 16
one: times 4 dd 1.0
Performance counter stats for './foo' (4 runs):
129.01 msec task-clock # 0.998 CPUs utilized ( +- 0.51% )
0 context-switches # 0.000 K/sec
0 cpu-migrations # 0.000 K/sec
2 page-faults # 0.016 K/sec
500,053,798 cycles # 3.876 GHz ( +- 0.00% )
50,000,042 branches # 387.576 M/sec ( +- 0.00% )
200,000,059 instructions # 0.40 insn per cycle ( +- 0.00% )
150,020,084 uops_issued.any # 1162.883 M/sec ( +- 0.00% )
150,014,866 uops_executed.thread # 1162.842 M/sec ( +- 0.00% )
0.129244 +- 0.000670 seconds time elapsed ( +- 0.52% )