GCC SSE 手写与生成

Posted

技术标签:

【中文标题】GCC SSE 手写与生成【英文标题】:GCC SSE Handwritten vs. Generated 【发布时间】:2019-12-02 16:02:22 【问题描述】:

我正在搞乱 SIMD 优化并编写了 3 个非常简单的向量类,并以 2 种不同的方式实现了加法,一种是手写组件,另一种是使用 _mm_add_ps https://godbolt.org/z/fPAERV。 有趣的是,GCC 不能(或者我没有正确地告诉它 x))使用 SSE 实现 vector2 的加法,只有在明确地向向量添加第四个浮点数之后(如在 vector3 中) gcc 使用 SEE 指令生成加法,即使我将向量对齐在 16 字节边界上。谁能告诉我为什么?

#include <xmmintrin.h>

struct alignas(16) vector final 
  union 
    struct 
      float x, y, z;
    ;
    float axes[3];
    __m128 v;
  ;

  vector(float x, float y, float z) noexcept : x(x), y(y), z(z) ;
  vector(__m128 v) noexcept : v(v);
;

vector operator+(const vector& v0, const vector& v1) noexcept 
  return _mm_add_ps(v0.v, v1.v);


struct alignas(16) vector2 final 
  union 
    struct 
      float x, y, z;
    ;
    float axes[3];
    __m128 v;
  ;

  vector2(float x, float y, float z) noexcept : x(x), y(y), z(z) ;
  vector2(__m128 v) noexcept : v(v);
;

vector2 operator+(const vector2& v0, const vector2& v1) noexcept 
  return v0.x + v1.x, v0.y + v1.y, v0.z + v1.z;


struct alignas(16) vector3 final 
  union 
    struct 
      float x, y, z, w;
    ;
    float axes[4];
    __m128 v;
  ;

  vector3(float x, float y, float z, float w) noexcept : x(x), y(y), z(z), w(w) ;
  vector3(__m128 v) noexcept : v(v);
;

vector3 operator+(const vector3& v0, const vector3& v1) noexcept 
  return v0.x + v1.x, v0.y + v1.y, v0.z + v1.z, v0.w + v1.w;

使用带有 -std=c++17 -O3 -Wall -Wextra 的 gcc9.2 生成的程序集

operator+(vector const&, vector const&):
        movaps  xmm1, XMMWORD PTR [rsi]
        addps   xmm1, XMMWORD PTR [rdi]
        movdqa  xmm0, xmm1
        movaps  XMMWORD PTR [rsp-24], xmm1
        movq    xmm1, QWORD PTR [rsp-16]
        ret
operator+(vector2 const&, vector2 const&):
        movss   xmm1, DWORD PTR [rdi+4]
        movss   xmm0, DWORD PTR [rdi+8]
        addss   xmm1, DWORD PTR [rsi+4]
        addss   xmm0, DWORD PTR [rsi+8]
        movss   xmm2, DWORD PTR [rdi]
        addss   xmm2, DWORD PTR [rsi]
        movss   DWORD PTR [rsp-20], xmm1
        movss   DWORD PTR [rsp-16], xmm0
        movq    xmm1, QWORD PTR [rsp-16]
        movss   DWORD PTR [rsp-24], xmm2
        movq    xmm0, QWORD PTR [rsp-24]
        ret
operator+(vector3 const&, vector3 const&):
        movaps  xmm0, XMMWORD PTR [rdi]
        addps   xmm0, XMMWORD PTR [rsi]
        movaps  XMMWORD PTR [rsp-40], xmm0
        mov     rax, QWORD PTR [rsp-32]
        movq    xmm0, QWORD PTR [rsp-40]
        movq    xmm1, rax
        mov     QWORD PTR [rsp-16], rax
        ret

【问题讨论】:

我将带有永久链接的代码添加到 Godbolt? 请将您的代码编辑到问题中。问题应该是自包含的,链接仅供参考。 你试过-ffast-math,或者至少-fno-trapping-math?如果未屏蔽,高元素中的垃圾可能会引发 FP 异常,因此默认的 -ftrapping-math 可能会因此而阻塞优化。但总的来说,编译器真的不喜欢做比你告诉他们更多的事情,即使这会导致错过优化,比如单独做所有 3 个。 这两个标志的输出相同 请注意,在 C++ 中使用union 进行类型双关语是未定义的行为(***.com/questions/11373203/…)——它可能在您的编译器上运行良好,但随时可能中断! 【参考方案1】:

“发明写入”通常是不允许的,并且会产生令人讨厌的编译器错误。 (因为线程安全,例如从另一个线程开始写入)。

即使它是联合对象的一部分,GCC 内部也可能将最​​后一个元素视为单独的,并且不愿意用“垃圾”来编写它。所以是的,这是一个错过的优化,您必须手动解决。


一般而言,SIMD 向量不太适合保存 3D 几何向量。理想情况下,您可以构建数据,以便您可以拥有四个 x 坐标的 __m128 x,以及四个 y 坐标中的另一个 __m128 y,等等。然后您可以在 3 个 addps 指令中进行 4 个向量加法。更好的是,执行 4 个向量长度或同时使用同一向量中的 x、y 和 z 的其他操作不涉及任何改组。

有关链接,请参阅 https://***.com/tags/sse/info,尤其是 Slides: SIMD at Insomniac Games (GDC 2015),其中详细介绍了有关有效使用 SIMD 的更多详细信息。

但是可以肯定的是,如果您已经针对可以不同方式布置数据的情况这样做了,那么可能仍然存在其他情况,即您只有几个单独的向量并且需要“float3”布局,并且仍然可以使用SIMD 也可以加快速度。

【讨论】:

以上是关于GCC SSE 手写与生成的主要内容,如果未能解决你的问题,请参考以下文章

手把手写C++服务器:编译实操——打开gcc/g++世界

野牛中的自动制作,可与手写的makefile一起正常工作

对服务器端接口的调用,自己手写了一个脚本,但返回信息的中文总是乱码(这个方法很不错,重要的是解决思路,寻找手写脚本与录制脚本 生成目录文件的区别)

利用mnist训练集生成的caffemodel对mnist测试集与自己手写的数字进行测试

深度卷积生成对抗网络DCGAN——生成手写数字图片

深度卷积生成对抗网络DCGAN——生成手写数字图片