【问题标题】:Helping GCC with auto-vectorisation帮助 GCC 进行自动矢量化
【发布时间】:2013-03-06 05:28:39
【问题描述】:

我有一个需要优化的着色器(包含大量矢量操作),我正在试验 SSE 指令以便更好地理解问题。

我有一些非常简单的示例代码。通过 USE_SSE 定义,它使用显式 SSE 内在函数;没有它,我希望 GCC 会为我完成这项工作。自动矢量化感觉有点挑剔,但我希望它可以为我节省一些头发。

编译器和平台是:gcc 4.7.1 (tdm64),目标 x86_64-w64-mingw32 和 Ivy Bridge 上的 Windows 7。

这是测试代码:

/*
    Include all the SIMD intrinsics.
*/
#ifdef USE_SSE
#include <x86intrin.h>
#endif
#include <cstdio>

#if   defined(__GNUG__) || defined(__clang__) 
    /* GCC & CLANG */

    #define SSVEC_FINLINE __attribute__((always_inline))

#elif defined(_WIN32) && defined(MSC_VER) 
    /* MSVC. */

    #define SSVEC_FINLINE __forceinline

#else
#error Unsupported platform.
#endif


#ifdef USE_SSE

    typedef __m128 vec4f;

    inline void addvec4f(vec4f &a, vec4f const &b)
    {
        a = _mm_add_ps(a, b);
    }

#else

    typedef float vec4f[4];

    inline void addvec4f(vec4f &a, vec4f const &b)
    {
        a[0] = a[0] + b[0];
        a[1] = a[1] + b[1];
        a[2] = a[2] + b[2];
        a[3] = a[3] + b[3];
    }

#endif

int main(int argc, char *argv[])
{
    int const count = 1e7;

    #ifdef USE_SSE
    printf("Using SSE.\n");
    #else
    printf("Not using SSE.\n");
    #endif

    vec4f data = {1.0f, 1.0f, 1.0f, 1.0f};

    for (int i = 0; i < count; ++i)
    {
        vec4f val = {0.1f, 0.1f, 0.1f, 0.1f};
        addvec4f(data, val);
    }

    float result[4] = {0};
    #ifdef USE_SSE
    _mm_store_ps(result, data);
    #else
    result[0] = data[0];
    result[1] = data[1];
    result[2] = data[2];
    result[3] = data[3];
    #endif

    printf("Result: %f %f %f %f\n", result[0], result[1], result[2], result[3]);

    return 0;
}

这是用以下方式编译的:

g++ -O3 ssetest.cpp -o nossetest.exe
g++ -O3 -DUSE_SSE ssetest.cpp -o ssetest.exe

除了显式 SSE 版本更快一点之外,输出没有任何差异。

这是循环的程序集,首先是显式 SSE:

.L3:
subl    $1, %eax
addps   %xmm1, %xmm0
jne .L3

它内联了调用。很好,或多或少只是直升_mm_add_ps

数组版本:

.L3:
subl    $1, %eax
addss   %xmm0, %xmm1
addss   %xmm0, %xmm2
addss   %xmm0, %xmm3
addss   %xmm0, %xmm4
jne .L3

它正在使用 SSE 数学,但在每个数组成员上。不是很理想。

我的问题是,我该如何帮助 GCC,使其更好地优化 vec4f 的数组版本?

任何特定于 Linux 的技巧也很有帮助,这是真正的代码将运行的地方。

【问题讨论】:

  • 请注意,float result[4] 在堆栈上可能不是 16 字节对齐的 - 它恰好在这种情况下工作,否则 _mm_store_ps 会出错。

标签: c++ gcc mingw sse vectorization


【解决方案1】:

Auto-vectorization with gcc 4.7 上的这篇 LockLess 文章无疑是我见过的最好的文章,我花了一段时间寻找类似主题的好文章。他们还有很多其他的articles,您可能会发现它们对处理各种低级软件开发的类似主题非常有用。

【讨论】:

  • 感谢您的链接,尝试了该文章中的提示,但似乎很难使其正确自动矢量化。现在尝试一个结构,强制为 16 字节对齐,但我能做的最好的是 4 addss 指令。
  • @Skurmedel 同意,当我更积极地扩展 X86 wikibook 的 SSE 部分时,我发现这篇文章和其他文章正在做研究。你看过内在函数吗?我一直在寻找我拥有的一些内在材料,但我似乎无法再挖掘它了。
【解决方案2】:

以下是基于您的代码的一些提示,可让 gcc 自动矢量化工作:

  • 将循环向上设为常量。为了向量化,GCC 需要将循环拆分 4 次迭代以适应 128 位长度的 SSE XMM 寄存器。 const 循环上限将帮助 GCC 确保循环有足够的迭代次数,并且向量化是有利可图的。
  • 删除 inline 关键字。如果代码被标记为内联,GCC 无法知道数组的起始点是否对齐,而无需通过-O3 开启的过程间分析。

    所以,为了使您的代码矢量化,您的 addvec4f 函数应修改如下:

    void addvec4f(vec4f &a, vec4f const &b)
    {
        int i = 0;
        for(;i < 4; i++)
          a[i] = a[i]+b[i];
    }
    

顺便说一句:

  • GCC 还有一些标志可以帮助您确定循环是否已被矢量化。 -ftree-vectorizer-verbose=2,数字越大,输出信息越多,目前可以是0,1,2Here是这个flag的文档,还有一些其他相关的flag。
  • 注意对齐。数组的地址要对齐,编译器不运行是无法知道地址是否对齐的。通常数据不对齐会出现bus errorHere 是原因。

【讨论】:

  • 谢谢,试试你的建议。
猜你喜欢
  • 1970-01-01
  • 2018-12-16
  • 1970-01-01
  • 2010-09-29
  • 1970-01-01
  • 2013-01-29
  • 2015-07-30
  • 2013-02-27
相关资源
最近更新 更多