为 `rsqrts` 包装器获取最少的指令

Getting Fewest Instructions for `rsqrtss` Wrapper

我认为是时候使用快速平方根倒数了。因此,我尝试编写一个函数(在生产中将被标记为 inline):

float sqrt_recip(float x) {
  return _mm_cvtss_f32( _mm_rsqrt_ss( _mm_set_ps1(x) ) ); //same as _mm_set1_ps
}

TL;DR:我的问题是 "how can I get GCC and ICC to output minimal assembly (two instructions) for the above function, preferably without resorting to raw assembly (sticking with intrinsics)?"

正如所写,在 ICC 13.0.1、GCC 5.2.0 和 Clang 3.7 上,输出是:

shufps  xmm0, xmm0, 0
rsqrtss xmm0, xmm0
ret

这是有道理的,因为我使用 _mm_set_ps1x 分散到寄存器的所有组件中。但是,我真的不需要那样做。我宁愿只做最后两行。当然,shufps只是一个循环。但是 rsqrtss 只有三到五个。 20% 到 33% 的开销完全没有价值。


我试过的一些东西:


三年半过去了,虽然编译器进步了,情况也变好了,但仍然没有输出最优代码。

然而,在不下降到原始汇编的情况下,我们仍然可以通过使用内联汇编比内部函数做得更好。我们必须小心一点;在非 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
}