(为什么?)simpe 循环不是矢量化的并且比 SIMD 计算慢吗?

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

(为什么?)即使使用

-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
较慢且未完全矢量化?我需要指定额外的编译器选项吗?

c++ optimization x86-64 compiler-optimization simd
1个回答
1
投票

为什么函数 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 实际上是做什么的?

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