AVX 512与AVX2的性能,适用于简单的阵列处理循环

Voj*_*zín 2 performance x86 micro-optimization avx2 avx512

我目前正在进行一些优化并比较DSP应用的矢量化可能性,这似乎是AVX512的理想选择,因为它们只是简单的不相关的阵列处理循环.但是在新的i9上,与AVX2相比,使用AVX512时没有任何合理的改进.有什么指针吗?有什么好结果吗?(顺便说一句.我试过MSVC/CLANG/ICL,没有明显的区别,很多时候AVX512代码实际上看起来比较慢)

Pet*_*des 7

这似乎太笼统了,但实际上有一些微体系结构细节值得一提。

请注意,AVX512-VL(向量长度)使您可以在128位和256位向量上使用新的AVX512指令(如打包的uint64_t<-> double转换,掩码寄存器等)。当调试Skylake-AVX512(又名Skylake-X)时,现代编译器通常使用256位矢量自动矢量化。例如gcc -march=nativegcc -march=skylake-avx512,除非您忽略调整选项以将值得折衷的代码的首选矢量宽度设置为512。请参阅@zam的答案。


vpxord ymm30, ymm29, ymm10在Skylake-X上使用512位向量(不是使用AVX512指令的256位)的一些主要事情是:

  • 使数据与矢量宽度对齐比使用AVX2更重要(每个未对齐的负载都越过缓存行边界,而不是遍历数组时彼此交叉)。实际上,它的作用更大。我完全忘记了我之前测试过的东西的确切结果,但可能是速度降低了20%,而由于未对准而降低了5%。

  • 运行512位uops将关闭端口1上的向量ALU(但不会关闭端口1上的整数执行单元)。某些Skylake-X CPU(例如Xeon Bronze)每个时钟仅具有1个512位FMA吞吐量,但是i7 / i9 Skylake-X CPU和高端Xeon在端口5上具有一个额外的512位FMA单元,该单元可以供电用于AVX512“模式”。

    因此,请做出相应的计划:从扩展到AVX512,您将不会获得双倍的速度,并且代码中的瓶颈现在可能在后端。

  • 运行512位uops还限制了您的最大Turbo速度,因此挂钟加速比内核时钟循环加速要低。Turbo缩减分为两个级别:完全是任何512位操作,然后是 512位,如持续的FMA。

  • 对于FP除法执行单元vsqrtps/pd zmmvdivps/pd充分的宽度; 它只有128位宽,因此div / sqrt与乘吞吐量的比率差了大约2倍。请参见浮点除法与浮点乘法。SKX的吞吐量为vsqrtps xmm/ymm/zmm每3/6/12周期之一。 double精度是相同的比率,但吞吐量和延迟较差。

    多达256位的YMM向量,延迟与XMM相同(sqrt为12个周期),但是对于512位的ZMM,延迟高达20个周期,耗时3 oups。(https://agner.org/optimize/用于说明表。)

    如果您遇到了除法器的瓶颈并且无法在混合中获得更多其他指令,那么VRSQRT14PS即使您需要进行牛顿迭代以获得足够的精度,也值得考虑。但请注意,AVX512的近似值1/sqrt(x)确实比AVX / SSE具有更多的保证精度位。)


至于自动矢量化,如果需要任何改组,则编译器在使用更宽的矢量时可能会做得更糟。对于简单的纯垂直的东西,编译器可以对AVX512进行处理。

您上一个问题有一个sin功能,也许如果编译器/ SIMD数学库仅具有该功能的256位版本,它将无法使用AVX512自动矢量化。

如果AVX512无法解决问题,则可能是内存带宽出现瓶颈。使用性能计数器进行分析并找出。或者尝试更多重复使用较小的缓冲区,并查看当您的数据在高速缓存中很热时,是否可以显着加快速度。如果是这样,请尝试缓存代码,或者通过对数据进行一次遍历来增加计算强度。

AVX512在i9上将理论最大FMA吞吐量提高了一倍(以及整数乘积,以及在同一执行单元上运行的许多其他事情),从而使DRAM与执行单元之间的失配大两倍。因此,更好地利用L2 / L1d缓存可以带来两倍的收益。

在数据已经加载到寄存器中的情况下处理数据是好的。

  • @PeterCordes-很有道理。我意识到我对拆分负载延迟了解不多,所以我[添加了一些测试](https://github.com/travisdowns/uarch-bench/commit/0a51d90b0ab37b8acc2a8704430cd389303d6148)。您可以像以前一样在[相同要点](https://gist.github.com/travisdowns/9d85a159631146dd04c8e743a6ef2195)中查看SKL和SKX上的结果。在Intel的L1中,拆分负载看起来有11个周期的延迟。[另一方面,AMD](https://travis-ci.org/travisdowns/uarch-bench/jobs/433762863#L997)在这种情况下似乎只有1个周期的罚款(总共5个周期)。 (2认同)
  • 对于其他高速缓存级别,仍然会有较大的损失,例如L2(Intel)的总周期为22-24,对于仅适合内存的较大大小,其损失在SKX上大于3倍,在SKL上也较大!这有点奇怪,因为您希望两行都以并行方式获取,并且这隐藏了大部分惩罚,但事实并非如此。在AMD Zen上,大区域的损失要少得多,但总体时序与Intel拆分情况一样慢,因此Intel确实擅长非拆分负载,或者测试有些不完善。 (2认同)

zam*_*zam 7

在ICL或GCC的情况下,您是如何编译(启用AVX512)代码的?AVX-512代码有两种"操作模式":

  1. 对于全新的英特尔编译器(从18.0/17.0.5开始),如果使用[Qa] xCORE-AVX512,您将只启用AVX-512-VL,它基本上意味着AVX512 ISA,但具有256 位宽的操作数.这似乎也是GCC的默认行为.
  2. 否则,如果(a)使用较旧的英特尔编译器,或(b)使用[Qa] xCOMMON-AVX512或(c)如果使用特殊新标志[Q/q] opt-zmm-usage = high,您将获得完整的AVX -512 ISA具有512位宽的操作数.(这里描述复杂的标志逻辑).在GCC的情况下,也可以使用-mprefer-vector-width = 512启用此模式.

如果你的代码是"AVX512友好的"(你有很长的矢量化代码序列,没有标量的代码片段"中断"向量指令序列),模式(2)是首选的,你必须启用它(这是不是默认的).

否则,如果您的代码不是AVX512友好(矢量代码之间的许多非矢量化代码片段),那么由于SKX"频率限制"AVX512VL有时可能更有益(至少在您进行更多代码矢量化之前)因此,您应确保以模式(1)运行.频率与ISA相关的景观例如在Lemier博士的博客中描述(尽管博客中给出的图片与现实相比有点过于夸张):https://lemire.me/blog/2018/09/07/avx- 512-when-to-to-use-these-new-instructions /https://lemire.me/blog/2018/08/13/the-dangers-of-avx-512-throttling-myth-or -现实/