(为什么?)即使使用
-03 and -march=native
进行编译,编译器也不使用 SIMD 指令来计算总和的简单循环吗?
考虑以下两个函数:
float sum_simd(const std::vector<float>& vec) {
__m256 a{0.0};
for (std::size_t i = 0; i < vec.size(); i += 8) {
__m256 tmp = _mm256_loadu_ps(&vec[i]);
a = _mm256_add_ps(tmp, a);
}
float res{0.0};
for (size_t i = 0; i < 8; ++i) {
res += a[i];
}
return res;
}
float normal_sum(const std::vector<float>& vec) {
float sum{0};
for (size_t i = 0; i < vec.size(); ++i) {
sum += vec[i];
}
return sum;
}
编译器似乎将求和变成:
vaddps ymm0, ymm0, ymmword ptr [rax + 4*rdx]
和
vaddss xmm0, xmm0, dword ptr [rcx + 4*rsi]
vaddss xmm0, xmm0, dword ptr [rcx + 4*rsi + 4]
vaddss xmm0, xmm0, dword ptr [rcx + 4*rsi + 8]
vaddss xmm0, xmm0, dword ptr [rcx + 4*rsi + 12]
vaddss xmm0, xmm0, dword ptr [rcx + 4*rsi + 16]
vaddss xmm0, xmm0, dword ptr [rcx + 4*rsi + 20]
vaddss xmm0, xmm0, dword ptr [rcx + 4*rsi + 24]
vaddss xmm0, xmm0, dword ptr [rcx + 4*rsi + 28]
当我在我的机器上运行这个程序时,我从 SIMD 总和中获得了显着的加速(约 10 倍)。 Godbolt 上也是如此。请参阅此处获取代码。
我使用 GCC 13 和 Clang 17 编译了程序并使用了选项
-O3 -march=native
。
为什么函数
normal_sum
较慢且未完全矢量化?我需要指定额外的编译器选项吗?
为什么函数 normal_sum 较慢且未完全矢量化?我需要指定额外的编译器选项吗?
是的。
-ffastmath
解决了这个问题(参见Godbolt)。这是带有此附加标志的主循环:
.L10:
vaddps ymm1, ymm1, YMMWORD PTR [rax] ; <---------- vectorized
add rax, 32
cmp rcx, rax
jne .L10
但是请注意,
-ffastmath
是几个更具体标志的组合。其中一些可能非常危险。例如,-funsafe-math-optimizations
和-ffinite-math-only
可能会破坏使用无穷大的现有代码或降低其精度。
有关这方面的更多信息,请阅读文章 gcc 的 ffast-math 实际上是做什么的?