提问人:Rasmus 提问时间:10/24/2023 最后编辑:Rasmus 更新时间:10/24/2023 访问量:74
为什么添加 vmovapd 指令可以使 simd 矢量化代码运行得更快?
Why adding vmovapd instruction makes simd vectorized code run faster?
问:
我正在对一些高性能数值代码进行矢量化,我注意到使用 Intel 的 SSE、AVX 和 AVX512 指令的 SIMD 矢量化性能与笔记本电脑上矢量寄存器的长度不成比例。我的笔记本电脑有 Tiger Lake 架构。我希望 AVX 的速度是 SSE 的两倍左右,AVX512 的速度是 AVX 的两倍左右。这是 x86-64 汇编中的一个玩具示例,类似于我正在开发的代码,其中注释掉了一条指令:
AVX512测试.s
.globl _start
.text
_start:
xor %edx, %edx
loop:
vmovapd %zmm17, %zmm18
vfmadd213pd %zmm5, %zmm6, %zmm18
vfmadd213pd %zmm4, %zmm17, %zmm18
vfmadd213pd %zmm3, %zmm17, %zmm18
vfmadd213pd %zmm2, %zmm17, %zmm18
vfmadd213pd %zmm1, %zmm17, %zmm18
vfmadd213pd %zmm0, %zmm17, %zmm18
# vmovapd %zmm17, %zmm19
vfmadd213pd %zmm15, %zmm16, %zmm19
vfmadd213pd %zmm14, %zmm17, %zmm19
vfmadd213pd %zmm13, %zmm17, %zmm19
vfmadd213pd %zmm12, %zmm17, %zmm19
vfmadd213pd %zmm11, %zmm17, %zmm19
vfmadd213pd %zmm10, %zmm17, %zmm19
vfmadd213pd %zmm9, %zmm17, %zmm19
vfmadd213pd %zmm8, %zmm17, %zmm19
vfmadd213pd %zmm7, %zmm17, %zmm19
vdivpd %zmm19, %zmm18, %zmm18
inc %edx
cmp $10000000, %edx
jne loop
movq $60, %rax
syscall
对于 AVX,我有相同的代码,其中 zmm 替换为 ymm,环路长度设置为 20000000,而对于 SSE,zmm 替换为 xmm,环路长度设置为 40000000。如果我取消注释注释的 vmovapd 操作,将其与 、链接 和 运行它,我得到:as -o AVX512test.o AVX512test.s
ld -o AVX512test.x AVX512.o
time ./AVX512test.x
上交所
real 0m0.090s
user 0m0.089s
sys 0m0.000s
AVX的
real 0m0.050s
user 0m0.049s
sys 0m0.000s
AVX512系列
real 0m0.058s
user 0m0.058s
sys 0m0.000s
因此,从 SSE 到 AVX 的扩展性很好,但从 AVX 到 AVX512 则不然。
如果我改为注释掉 vmovapd 指令,我会得到:
上交所
real 0m0.351s
user 0m0.351s
sys 0m0.000s
AVX的
real 0m0.189s
user 0m0.189s
sys 0m0.000s
AVX512系列
real 0m0.109s
user 0m0.109s
sys 0m0.000s
因此,如果没有第二个 vmovapd 操作,计算速度会明显变慢,但另一方面,正如我所期望的那样,它会随着矢量化而扩展。
我的问题是,为什么删除操作会使代码运行速度变慢,为什么 AVX512 不比使用 vmovapd 操作的 AVX 快?
我尝试以不同的方式修改代码,例如在循环体中使用更少和更多的指令,并让所有指令都相同。但是我发现唯一影响性能和矢量寄存器长度缩放显着的是其他指令中是否存在 vmovapd 指令。我有点无知,但想知道它是否与无序执行有关。如果可能的话,我希望在 512 位寄存器上矢量化的代码的速度大约是在 256 位寄存器上矢量化的两倍左右,并且没有注释 vmovapd 指令。
答:
100 毫秒对于一个好的基准来说太少了。至少要一两秒钟。也就是说,使用您注释掉的指令,指令定位可以与之前的指令并行运行,而如果没有指令,则不能。这解释了总持续时间的差异。zmm19
在许多微架构上,AVX-512 可以在比 AVX 和 SSE 更少的端口上运行。具体来说,AVX 和 SSE 指令可以在端口 p0、p1 和 p5 上运行,而 AVX-512 一起使用端口 p0 和 1(另一个非 SIMD 指令可以在 p1 上运行,而 p0+p1 用于 AVX-512)或 p5。
FMA 指令在 Tigerlake 客户端的端口 p0 和 p1 上运行。这意味着使用 SSE 和 AVX,每个周期可以运行两个 FMA,而使用 AVX-512 只能运行一个 FMA。由于 AVX-512 的矢量宽度是其两倍,这意味着只要您的数值内核允许至少两个并行的 FMA 运算,它们每个周期执行相同数量的 FLOP。
如果按照指示保留移动指令,则代码就是这种情况,因为循环的多次迭代是独立的。如您所见,AVX2 和 AVX-512 的性能将非常相似。但是,如果注释掉指令,则现在迭代是通过依赖链耦合的,并且必须按顺序执行。如果您的代码限制为每个周期只有一个 FMA,则 AVX-512 将比 AVX2 更快。
我的建议是:优化代码以获得更高的指令级并行性 (ILP)。AVX-512 是此应用程序的不错选择,您应该继续使用它。当您在服务器 CPU(Xeon Gold 类)上执行此代码时,p01 和 p5 都可以执行 FMA 指令,并且一旦您重写 AVX-512 以允许更高的 ILP,代码将更快。
评论
vfmadd213pd
zmm18
zmm19
zmm19
评论
vmovapd %zmm17, %zmm19
通过 ZMM19 断开循环携带的依赖链。将您的 asm 复制/粘贴到 uica.uops.info 中,让它针对 Tiger Lake 对其进行分析,并生成依赖关系图。