为何GCC、ICX、Clang在Intel处理器上不自动使用AVX-512指令向量化代码,却在AMD处理器上支持?
为何GCC、ICX、Clang在Intel处理器上不自动使用AVX-512指令向量化代码,却在AMD处理器上支持?
先来看我的测试代码,逻辑非常简单:
void x(float* array, float const LOW_THRESHOLD, float const HIGH_THRESHOLD) noexcept { for ( int index = 0; index < 16; ++index ) { array[ index ] = LOW_THRESHOLD < array[ index ] && array[ index ] < HIGH_THRESHOLD ? 1.0f : 0.0f; } }
这段代码简直就是为AVX-512向量化优化设计的“样板代码”,怎么看都应该被编译器自动转换成向量指令。
我用编译器探索工具做了测试,结果却有点出乎意料:Clang、ICX和GCC只在AMD架构上会自动做AVX-512向量化——具体来说是znver4和znver5(znver3因为本身不支持AVX512,自然不在此列)。
而针对Intel的三款支持AVX512的处理器:
- Skylake AVX512
- Granite Rapids
- Emerald Rapids
我都用了工具上的最新版本编译器进行测试,结果都没有生成AVX512的向量化代码。
举两个具体的编译结果对比:
Clang编译Intel Emerald Rapids(编译选项:-O3 -march=emeraldrapids)
生成的汇编代码是:
.LCPI0_0: .long 0x3f800000 x(float*, float, float): vbroadcastss ymm0, xmm0 vmovups ymm2, ymmword ptr [rdi] vmovups ymm3, ymmword ptr [rdi + 32] vcmpltps k1, ymm0, ymm2 vbroadcastss ymm1, xmm1 vcmpltps k1 {k1}, ymm2, ymm1 vbroadcastss ymm2, dword ptr [rip + .LCPI0_0] vmovaps ymm4 {k1} {z}, ymm2 vmovups ymmword ptr [rdi], ymm4 vcmpltps k1, ymm0, ymm3 vcmpltps k1 {k1}, ymm3, ymm1 vmovaps ymm0 {k1} {z}, ymm2 vmovups ymmword ptr [rdi + 32], ymm0 vzeroupper ret
Clang编译AMD Ryzen v5(编译选项:-O3 -march=znver5)
生成的汇编代码则是:
.LCPI0_0: .long 0x3f800000 x(float*, float, float): vmovups zmm2, zmmword ptr [rdi] vbroadcastss zmm0, xmm0 vbroadcastss zmm1, xmm1 vcmpltps k1, zmm0, zmm2 vcmpltps k1 {k1}, zmm2, zmm1 vbroadcastss zmm0 {k1} {z}, dword ptr [rip + .LCPI0_0] vmovups zmmword ptr [rdi], zmm0 vzeroupper ret
一开始我以为是编译器漏掉了这个优化,但仔细想想又觉得不对劲:ICX可是Intel自家的编译器,而且Intel和AMD的工程师都参与了Clang、GCC这些开源编译器的开发,这种明显的优化点被漏掉的概率其实很低。
那问题来了:是不是Intel架构上并不推荐使用AVX-512?这会不会就是编译器不在Intel处理器上自动触发向量化的原因?
(注:测试代码来自Stack Overflow的相关提问,感谢Terrordrone提供的示例代码)
另外,后来发现添加编译选项-mprefer-vector-width=512后,编译器就会在Intel架构上生成AVX512的向量化代码了,这个方法可以解决这个问题。
备注:内容来源于stack exchange,提问作者pratikpc
相关产品推荐
相关产品推荐

