Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/135.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 静态/静态本地SSE/AVX变量是否阻塞xmm/ymm寄存器?_C++_Sse_Avx - Fatal编程技术网

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

C++ 静态/静态本地SSE/AVX变量是否阻塞xmm/ymm寄存器?,c++,sse,avx,C++,Sse,Avx,使用SSE内部函数时,通常需要零向量。避免在函数被调用时(每次有效地调用某个xor向量指令)在函数内部创建零变量的一种方法是使用静态局部变量,如 static inline __m128i negate(__m128i a) { static __m128i zero = __mm_setzero_si128(); return _mm_sub_epi16(zero, a); } 似乎只有在第一次调用函数时才初始化变量。(我通过调用一个true函数而不是yMMJStEnZoosis

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

static inline __m128i negate(__m128i a)
{
   static __m128i zero = __mm_setzero_si128();
   return _mm_sub_epi16(zero, a);
}
似乎只有在第一次调用函数时才初始化变量。(我通过调用一个true函数而不是yMMJStEnZoosisi128)来检查它,它似乎只可能在C++中,而不是在C中。 (1) 然而,一旦初始化发生:这个块是否为程序的其余部分阻塞了xmm寄存器

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

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

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

static __m128i zero = _mm_setzero_si128();
(4) 当程序运行时,全局变量是否会保留在xmm寄存器中

非常感谢你的帮助


(由于这也适用于AVX intrinsic,因此我还添加了AVX标记。)

由于使用向量提高效率,因此代码存在问题

未使用常量初始化的静态变量将在运行时初始化。以线程安全的方式。第一次调用内联函数时,静态变量将被初始化。之后每次调用时,都会检查静态变量是否需要初始化

所以在每次通话中,都会有一个检查,然后是内存加载。如果不使用静态变量,则可能只有一条指令创建值,还有大量优化机会。从内存加载很慢


您可以拥有任意多的静态变量。编译器将处理您向它抛出的任何内容

回答这里真正应该问的问题:你根本不应该担心这个问题。通过
xor
对寄存器进行调零在大多数情况下都不会产生任何成本。现代x86处理器认识到这一习惯用法,并直接在寄存器重命名中处理零;根本不需要发布任何µop。唯一能让你慢下来的是如果你被前端束缚住了,但这是一种非常罕见的情况


虽然在其他情况下,这些问题的变化可能值得思考(Mystical的评论提供了一些关于如何自己回答这些问题的好线索),但你真的应该使用
setzero
,到此为止。

关于你应该在Stephen Canon执行的特定操作

static inline Vec8s operator - (Vec8s const & a) {
    return _mm_sub_epi16(_mm_setzero_si128(), a);
}
那是直接从我的照片上取的

<>但是让我们考虑一下<代码>静态< /COD>关键字。当您使用
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));
}
foo函数

    movdqa  xmm1, XMMWORD PTR _ZL4zero[rip]
    psubw   xmm1, xmm0
    movdqa  xmm0, xmm1
因此GCC从不为静态变量使用XMM寄存器。它从数据部分的内存中读取数据

如果我们做了
\u mm\u sub\u epi16(\u mm\u setzero\u si128(),a)
,会怎么样?然后GCC为
foo

    pxor    xmm1, xmm1
    psubw   xmm1, xmm0
    movdqa  xmm0, xmm1
    movdqa  xmm1, XMMWORD PTR .LC0[rip]
    psubw   xmm1, xmm0
    movdqa  xmm0, xmm1
在英特尔处理器上,自Sandy Bridge以来,
pxor
是“免费”的。在之前的处理器上,它几乎是免费的。因此,这显然是一个比从内存中读取更好的解决方案

如果我们尝试
\u mm\u sub\u epi16(\u mm\u set1\u epi32(-1),a)
,会怎么样。在这种情况下,GCC产生

    pcmpeqd xmm1, xmm1
    psubw   xmm1, xmm0
    movdqa  xmm0, xmm1
在任何处理器上,
pcmpeqd
指令都不是免费的,但它仍然比使用
movdqa
从内存中读取要好。好的,所以
0
-1
是特殊的。那么
\u mm\u sub\u epi16(\u mm\u set1\u epi32(1)
)呢?在这种情况下,GCC为
foo

    pxor    xmm1, xmm1
    psubw   xmm1, xmm0
    movdqa  xmm0, xmm1
    movdqa  xmm1, XMMWORD PTR .LC0[rip]
    psubw   xmm1, xmm0
    movdqa  xmm0, xmm1
这与使用静态变量基本相同!当我查看这些节时,我看到.LC0指向一个只读数据节(.rodata)

编辑:下面是一种使用GCC的方法

寄存器uum128i零asm(“xmm15”)=mm_set1_epi32(1)

这就产生了

movdqa  xmm2, xmm15
psubw   xmm2, xmm0
movdqa  xmm0, xmm2

我想我可以在讨论中添加一个有趣的观点,特别是我对_mm_abs_ps()的评论。如果我定义

static inline __m128 _mm_abs_ps_2(__m128 x) {
  __m128 signMask = _mm_set1_ps(-0.0F);
  return _mm_andnot_ps(signMask, x);
}
(Agner Fog的VCL使用整数set1、强制转换和and操作,但实际上应该是相同的)并在循环中使用该函数

float *p = data;
for (int i = 0; i < LEN; i += 4, p += 4)
  _mm_store_ps(p, _mm_abs_ps_2(_mm_load_ps(p)));

因此,在大多数情况下,可能根本不必担心在某个函数中重复将某个xmm寄存器设置为常量。

1和2,创建16个包含16个静态的函数,然后查看是否编译并运行。4、创建16个全局变量,看看它是否编译并运行。3受到编译器优化的影响。(顺便说一句,您正在进行微优化,因为在当前处理器上,寄存器归零基本上是免费的)如果您正在广播一个非零值,例如
\u mm\u set1\u epi32(-1)
,您的问题会更有趣。事实证明
-1
也很特别。所以除了
0
-1
之外,广播任何东西都会很有趣。e、 g.
\u mm\u set1\u epi32(1)
。感谢@Zboson提供两个有用的链接。关于你最后的评论:为什么-1也很特别?此外,我还查看了广播操作(至少对于-mssse3而言),它们似乎非常昂贵(编译为两条指令,其中一条指令是矢量混洗指令)。我不知道(或者我忘了)这是通过寄存器重命名免费处理的。那真的很酷。我读了一点,很明显SB有一个物理零寄存器,它只是将寄存器重命名为零寄存器,这就是为什么它不需要一个µop。我想这只适用于SB,因此在Nehalem上调零至少需要1µop?是的,调零仅在SNB上“免费”。梅罗姆·韦斯特默