获取最简单的`rsqrtss` Wrapper说明

ima*_*ett 6 c++ assembly gcc icc

我想是时候使用快速倒数平方根.所以,我尝试编写一个函数(将inline在生产中标记):

float sqrt_recip(float x) {
  return _mm_cvtss_f32( _mm_rsqrt_ss( _mm_set_ps1(x) ) ); //same as _mm_set1_ps
}
Run Code Online (Sandbox Code Playgroud)

TL; DR:我的问题是"如何让GCC和ICC为上述函数输出最小的汇编(两条指令),最好不要求助于原始汇编(坚持使用内在函数)?"

如上所述,在ICC 13.0.1,GCC 5.2.0和Clang 3.7上,输出为:

shufps  xmm0, xmm0, 0
rsqrtss xmm0, xmm0
ret
Run Code Online (Sandbox Code Playgroud)

这是有道理的,因为我曾经_mm_set_ps1分散x到寄存器的所有组件中.但是,我真的不需要这样做.我宁愿只做最后两行.当然,shufps只有一个周期.但rsqrtss只有三到五个.这是20%到33%的开销,完全没有价值.


我试过的一些事情:

  • 我试着不设置它:
    union { __m128 v; float f[4]; } u;
    u.f[0] = x;
    return _mm_cvtss_f32(_mm_rsqrt_ss(u.v));
    这实际上适用于Clang,但ICC和GCC的输出尤其令人震惊.

  • 您可以填充零(即使用_mm_set_ss),而不是散射.同样,GCC和ICC的输出都不是最佳的.在海湾合作委员会的案例中,海湾合作委员会搞笑地补充说:
    movss DWORD PTR [rsp-12], xmm0
    movss xmm0, DWORD PTR [rsp-12]


ima*_*ett 2

三年半过去了,虽然编译器已经进步,情况也有所好转,但它们仍然没有输出最佳代码。

然而,在不使用原始汇编的情况下,我们仍然可以通过使用内联汇编比内在函数做得更好。我们必须要小心一些;在非 VEX 编码和 VEX 编码指令之间切换会带来很大的损失,因此我们需要两个代码路径。

这会在 GCC (9.0.1)、Clang (9.0.0) 和 ICC (19.0.1.144) 上产生最佳结果。它仅在内联且未进行 VEX 编码时在 MSVC (19.16) 上产生最佳结果(这可能是我们所能做到的,因为 MSVC 不支持 x86-64 上的内联汇编):

#include <xmmintrin.h>


inline float rsqrt_fast(float x) {
    #ifndef _MSC_VER //Optimal
        float result;
        asm( //Note AT&T order
            #ifdef __AVX__
            "vrsqrtss %1, %1, %0"
            #else
            "rsqrtss %1, %0"
            #endif
            : "=x"(result)
            : "x"(x)
        );
        return result;
    #else //TODO: not optimal when in AVX mode or when not inlined
        return _mm_cvtss_f32(_mm_rsqrt_ss(_mm_set_ps1(x)));
    #endif
}
Run Code Online (Sandbox Code Playgroud)