如何让 VC 编译器使用 SIMD 更好地优化我的代码?

Posted

技术标签:

【中文标题】如何让 VC 编译器使用 SIMD 更好地优化我的代码?【英文标题】:How to make VC compiler optimize my code with SIMD better? 【发布时间】:2017-01-14 07:51:59 【问题描述】:

我现在正在学习 SIMD,并考虑如何让编译器更好地优化我的代码。现在我正在使用 Visual C++ 2013 x86。

我有一个数组,我有另一个数组,我想这样计算:

void computeSum(float* __restrict arr, float* __restrict inp1, float* __restrict inp2, int count)

    __declspec(align(16)) float* p1 = inp1;
    __declspec(align(16)) float* p2 = inp2;
    __declspec(align(16)) float* ret = arr;

    while (count > 0)
    
        ret[0] = p1[0] + p2[0];
        ret[1] = p1[1] + p2[1];
        ret[2] = p1[2] + p2[2];
        ret[3] = p1[3] + p2[3];

        p1 += 4;
        p2 += 4;
        ret += 4;

        count -= 4;
    

我想告诉编译器数组与 16 字节边界对齐,并且任何人都没有覆盖另一个,一个循环将计算 4 个连续浮点数的总和。

但在生成的代码中,VC 更喜欢 MOVSS/ADDSS 而不是我希望的 ADDPS。

如果我将项目配置为使用 LLVM-vs2013 工具链,它会使用 ADDPS 来计算总和。

我知道如何使用编译器内部函数来编写 SIMD 代码,但这不是我想要的。

VC使用ADDPS指令还有什么提示吗?

这是完整的代码。

#include <stdio.h>
#include <stdlib.h>

void computeSum(float* __restrict arr, float* __restrict inp1, float* __restrict inp2, int count)

    __declspec(align(16)) float* p1 = inp1;
    __declspec(align(16)) float* p2 = inp2;
    __declspec(align(16)) float* ret = arr;

    while (count > 0)
    
        ret[0] = p1[0] + p2[0];
        ret[1] = p1[1] + p2[1];
        ret[2] = p1[2] + p2[2];
        ret[3] = p1[3] + p2[3];

        p1 += 4;
        p2 += 4;
        ret += 4;

        count -= 4;
    


int main()

    float* inp1 = (float*)_aligned_malloc(sizeof(float) * 128, 16);
    float* inp2 = (float*)_aligned_malloc(sizeof(float) * 128, 16);
    float* result = (float*)_aligned_malloc(sizeof(float) * 128, 16);

    for (int i = 0; i < 128; ++i)
    
        inp1[i] = inp2[i] = i;
    

    computeSum(result, inp1, inp2, 128);

    for (int i = 0; i < 128; ++i)
    
        printf("%f\t", result[i]);
    

    return 0;

【问题讨论】:

您是否指定了 /arch:SSE(或类似名称)? @harold 是的,开关 /arch:SSE2 已启用。 您是否尝试过 not 展开循环? @YvesDaoust 不,我没有。谢谢你的建议。现在我尝试了( for(int i = 0; i 你试过单循环吗? 【参考方案1】:

Visual C++ 2013 或更高版本将默认为 x86 使用 /arch:SSE2,但您仍应检查 Visual Studio 项目中的设置,以确保它没有明确设置为其他内容。对于 x64,/arch:SSE2 是隐式的。

Visual C++ 唯一一次自动生成多通道(如ADDPS)而不是单通道(ADDSS)指令是由于自动矢量化器。有关详细信息,请参阅 MSDN 并特别注意 /Qvec-report:2 开关 - 请注意,在禁用优化时不会发生这种情况,这在调试配置中很常见。

大多数 SIMD(多通道)代码生成都可以通过显式使用来更好地完成。有关这种编码风格的大量示例,请参阅DirectXMath。

【讨论】:

了解它被称为“自动矢量化器”和启用报告的参数开关对我来说非常重要。但经过一些试验后,我仍然无法让优化器使用 addps。你能提供一段示例代码吗?谢谢

以上是关于如何让 VC 编译器使用 SIMD 更好地优化我的代码?的主要内容,如果未能解决你的问题,请参考以下文章

如何编写编译器可以针对 SIMD 比较优化的代码? [复制]

为新指令集扩展优化编译的代码的向后兼容性

如何让我的视图更好地保存 Django

如何在 Visual Studio 2015(用于 C++)中仅禁用 SIMD 自动矢量化优化?

为啥 OpenMP 'simd' 比 'parallel for simd' 有更好的性能?

.net core SIMD范例分析