c++ - 如何使msvc向量化浮点加法?

标签 c++ optimization vectorization

我有这个代码:

constexpr size_t S = 4;
void add(std::array<float, S>& a, std::array<float, S> b)
{
    for (size_t i = 0; i < S; ++i)
        a[i] += b[i];
}

clang 和 gcc 都意识到,他们可以使用 addps 指令进行一次打包加法,而不是进行 4 次单次加法。例如。 clang 生成这个:

movups  xmm2, xmmword ptr [rdi]
movlhps xmm0, xmm1              # xmm0 = xmm0[0],xmm1[0]
addps   xmm0, xmm2
movups  xmmword ptr [rdi], xmm0
ret

正如您在 godbolt 上看到的那样, gcc 有点落后于 clang,因为它需要更多的 Action 。不过没关系。我的问题是 msvc,如您所见,它更糟:

mov     eax, DWORD PTR _a$[esp-4]
movups  xmm2, XMMWORD PTR _b$[esp-4]
movss   xmm1, DWORD PTR [eax+4]
movaps  xmm0, xmm2
addss   xmm0, DWORD PTR [eax]
movss   DWORD PTR [eax], xmm0
movaps  xmm0, xmm2
shufps  xmm0, xmm2, 85                          ; 00000055H
addss   xmm1, xmm0
movaps  xmm0, xmm2
shufps  xmm0, xmm2, 170                   ; 000000aaH
shufps  xmm2, xmm2, 255                   ; 000000ffH
movss   DWORD PTR [eax+4], xmm1
movss   xmm1, DWORD PTR [eax+8]
addss   xmm1, xmm0
movss   xmm0, DWORD PTR [eax+12]
addss   xmm0, xmm2
movss   DWORD PTR [eax+8], xmm1
movss   DWORD PTR [eax+12], xmm0
ret     0

我尝试了不同的优化级别,但 /O2 似乎是最好的。我还尝试手动展开循环,但 msvc 没有任何变化。

那么,有没有办法让 msvc 做同样的优化,使用一个 addps 而不是四个 adds?或者 msvc 不这样做是否有充分的理由?

编辑

按照 Shawn 在评论中的建议添加 /Qvec-report:2 标志(谢谢!)我发现 msvc 认为循环太小,无法从矢量化中获益。 Clang 和 gcc 有不同意见,但是 OK。 事实上,如果我将 S 更改为 16,msvc 会提供一个矢量化版本,即使它仍然提供一个非矢量化分支(在我看来完全没有必要,因为 S 在编译时已知)。一般来说,与 gcc 和 clang 相比,msvc 的代码看起来一团糟,参见 here .

最佳答案

我已经测试了您在 Microsoft Visual Studio 2017 中发布的代码,它适用于我。当我用 aligned 调用你的函数 add 时和非 aliased参数,您的函数 add 编译为 addps 指令,而不是 addss。也许您使用的是旧版本的 Visual Studio?

但是,通过故意为函数提供非对齐或别名参数,我能够重现您的问题。为了实现这一点,我用 C 风格的数组指针替换了函数参数(因为我不知道 std::array 究竟是如何实现的)并故意用别名指针调用函数,通过使两个阵列重叠。在这种情况下,生成的代码会调用 addss 四次,而不是调用一次 addps。故意传递未对齐的指针具有相同的效果。

这种行为也是有道理的。为了使向量化有意义,编译器必须确保数组不重叠并且正确对齐。我相信与 SSE 相比,对齐对于 AVX 来说不是一个问题。

当然,编译器必须能够在编译时而非运行时确定是否存在可能的别名或对齐问题。因此,问题可能在于您以编译器无法在编译时确定参数是否别名以及参数是否对齐的方式调用函数。编译器有时在确定这些事情时不是很聪明。但是,正如您在评论部分所指出的那样,由于您是按值传递一个参数,因此编译器应该能够确定不存在重叠的危险。因此,我猜测这是一个对齐问题,因为编译器在编译时不确定 std:array 的内容是如何对齐的。由于我无法使用 std::array 重现您的问题,您可能想发布有关如何调用该函数的代码。

您还可以通过显式调用相应的编译器内部函数来强制执行向量化 _mm_add_ps对于指令 addps

关于c++ - 如何使msvc向量化浮点加法?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58295522/

相关文章:

c++ - 我应该在 C++ 中内联它吗?

C++ Performance 从磁盘写入和读取

递归函数的代码优化

matlab - 如何给三维矩阵赋值

python - 什么是矢量化?

python - 如何在numpy中按索引累积数组?

c++ - 为 opencv 项目创建 makefile

c++ - 模板类中模板方法的友元,在模板类中

optimization - LLVM 优化级别的含义

c++ - 模板(元)编程是否总是只有一种实现方式?