是静态/静态本地SSE/AVX变量阻塞xmm/ymm寄存器?

Ral*_*alf 6 c++ sse avx

使用SSE内在函数时,通常需要零向量.无论何时调用函数(每次有效调用某些xor向量指令),避免在函数内部创建零变量的一种方法是使用静态局部变量,如

static inline __m128i negate(__m128i a)
{
   static __m128i zero = __mm_setzero_si128();
   return _mm_sub_epi16(zero, a);
}
Run Code Online (Sandbox Code Playgroud)

似乎只在第一次调用函数时才初始化变量.(我通过调用一个真正的函数而不是_mm_setzero_si128()内在函数来检查这个.顺便说一句,它似乎只能在C++中,而不是在C中.)

(1)但是,一旦发生这种初始化:这是否阻止了xmm寄存器用于程序的其余部分?

(2)更糟糕的是:如果在多个函数中使用这样的静态局部变量,它会阻塞多个xmm寄存器吗?

(3)反过来说:如果它没有阻塞xmm寄存器,那么在调用函数时,零变量是否总是从内存中重新加载?那么静态局部变量将毫无意义,因为使用_mm_setzero_si128()会更快.

作为替代方案,我考虑将零置于一个全局静态变量中,该变量将在程序启动时初始化:

static __m128i zero = _mm_setzero_si128();
Run Code Online (Sandbox Code Playgroud)

(4)程序运行时,全局变量是否会保留在xmm寄存器中?

非常感谢你的帮助!

(由于这也适用于AVX内在函数,我还添加了AVX标记.)

Ste*_*non 10

回答这个真正应该问这里的问题是:你不应该担心这个在所有.xor在大多数情况下,通过有效地对寄存器进行归零无需任何费用.现代x86处理器识别这个习惯用法并直接在寄存器重命名中处理归零; 没有μop需要发布.只有你受到前端的约束,这才能减慢你的速度,但这是一个相当罕见的情况.

虽然这些问题的变化可能值得在其他情况下进行思考(而Mystical的评论给出了一些如何自己回答它们的良好线索),你应该真正使用setzero并称之为一天.


Z b*_*son 5

关于这个特殊的操作你应该在Stephen Canon说和做

static inline Vec8s operator - (Vec8s const & a) {
    return _mm_sub_epi16(_mm_setzero_si128(), a);
}
Run Code Online (Sandbox Code Playgroud)

这是直接从Agner Fog的Vector Class Library中获取的.

但是让我们考虑static关键字的作用.static使用静态存储声明变量时.这将它放在目标文件的数据部分(包括.bss部分)中.

#include <x86intrin.h>
extern "C" void foo2(__m128i a);

static const __m128i zero = _mm_setzero_si128();

static inline __m128i negate(__m128i a) {
    return _mm_sub_epi16(zero, a);
}

extern "C" void foo(__m128i a, __m128i b) {
    foo2(negate(a));
}
Run Code Online (Sandbox Code Playgroud)

我这样做g++ -O3 -c static.cpp,然后看看diassembly和部分.我看到有一个带标签的.bss部分_ZL4zero.然后有一个代码启动部分,它将静态变量写入.bss部分.

.text.startup
    pxor    xmm0, xmm0
    movaps  XMMWORD PTR _ZL4zero[rip], xmm0
    ret
Run Code Online (Sandbox Code Playgroud)

foo功能

    movdqa  xmm1, XMMWORD PTR _ZL4zero[rip]
    psubw   xmm1, xmm0
    movdqa  xmm0, xmm1
Run Code Online (Sandbox Code Playgroud)

所以GCC从不使用XMM寄存器作为静态变量.它从数据部分的内存中读取.

如果我们做了_mm_sub_epi16(_mm_setzero_si128(),a)什么?然后GCC生产foo

    pxor    xmm1, xmm1
    psubw   xmm1, xmm0
    movdqa  xmm0, xmm1
Run Code Online (Sandbox Code Playgroud)

在Sandy Bridge之后的英特尔处理器上pxor是"免费的".在之前的处理器上它几乎是免费的.所以这显然是比从内存中读取更好的解决方案.

如果我们尝试_mm_sub_epi16(_mm_set1_epi32(-1),a).在那种情况下,GCC生产

    pcmpeqd xmm1, xmm1
    psubw   xmm1, xmm0
    movdqa  xmm0, xmm1
Run Code Online (Sandbox Code Playgroud)

pcmpeqd指令不在free任何处理器上,但它仍然比使用内存读取更好movdqa.好的,所以0-1很特别.怎么样_mm_sub_epi16(_mm_set1_epi32(1))?在这种情况下,GCC生产foo

    movdqa  xmm1, XMMWORD PTR .LC0[rip]
    psubw   xmm1, xmm0
    movdqa  xmm0, xmm1
Run Code Online (Sandbox Code Playgroud)

这与使用静态变量基本相同!当我看到这些部分时,我看到.LC0指向一个只读数据部分(.rodata).

编辑:这里有一种让GCC使用的方法是在寄存器中使用全局变量.

register __m128i zero asm("xmm15")= _ mm_set1_epi32(1);

这产生了

movdqa  xmm2, xmm15
psubw   xmm2, xmm0
movdqa  xmm0, xmm2
Run Code Online (Sandbox Code Playgroud)