为什么添加 vmovapd 指令可以让 simd 矢量化代码运行得更快?

问题描述 投票:0回答:1

我正在尝试一些高性能数字代码的矢量化,我注意到使用英特尔的 SSE、AVX 和 AVX512 指令的 SIMD 矢量化的性能不会随着笔记本电脑上矢量寄存器的长度而变化。我的笔记本电脑采用 Tiger Lake 架构。我希望 AVX 的速度大约是 SSE 的两倍,AVX512 的速度大约是 AVX 的两倍。这是 x86-64 汇编中的一个玩具示例,类似于我正在开发的代码,其中注释掉了一条指令:

AVX512test.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 位寄存器上矢量化的代码速度大约是在未注释 vmovapd 指令的情况下在 256 位寄存器上矢量化的代码的两倍。

assembly simd microbenchmark avx512
1个回答
0
投票

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 时只能运行一个。由于 AVX-512 的向量宽度是两倍,这意味着只要核心允许并行至少两个 FMA 操作,它们每个周期执行相同数量的 FLOP。

如果您按照指示注释掉移动指令,则您的代码就是这种情况,然后两个 FMA 组是独立的。正如您所观察到的,AVX2 和 AVX-512 的性能将非常相似。但是如果添加指令,现在这两个组通过依赖链耦合并且必须顺序执行。如果您的代码每个周期仅限一个 FMA,AVX-512 将比 AVX2 更快。

我的建议:优化代码以获得更高的指令级并行性(ILP)。 AVX-512 是此应用程序的不错选择,您应该继续使用它。当您在服务器 CPU(Xeon Gold 级)上执行此代码时,p01 和 p5 都可以执行 FMA 指令,并且一旦您重写代码以允许更高的 ILP,使用 AVX-512 代码将会更快。

© www.soinside.com 2019 - 2024. All rights reserved.