C 使用内部函数初始化uu m128i常量的最快方法?
目前,我有一个uu m128i变量,我们称它为C 使用内部函数初始化uu m128i常量的最快方法?,c,visual-c++,sse,intrinsics,micro-optimization,C,Visual C++,Sse,Intrinsics,Micro Optimization,目前,我有一个uu m128i变量,我们称它为X。我想用一个128位的常量值对其进行异或运算,然后将值保存回X。因此,本质上,对于某个常数C,X^=C 目前,我正在做以下工作: X=_-mm\u-xor\u-si128(X,_-mm\u-set\u-epi64x(C\u-a,C\u-b)) 它从异或的C的两个64位部分构建一个\uuuu m128i 我的问题是,这似乎不是为xor初始化_m128i常量的最有效方法。尝试从一个对齐的数组加载会更好吗?还是其他方法 我目前在Visual Studio
X
。我想用一个128位的常量值对其进行异或运算,然后将值保存回X
。因此,本质上,对于某个常数C
,X^=C
目前,我正在做以下工作:
X=_-mm\u-xor\u-si128(X,_-mm\u-set\u-epi64x(C\u-a,C\u-b))
它从异或的C
的两个64位部分构建一个\uuuu m128i
我的问题是,这似乎不是为xor初始化_m128i常量的最有效方法。尝试从一个对齐的数组加载会更好吗?还是其他方法
我目前在Visual Studio中与MSVC合作。这个答案纯粹是关于常量
C
的情况。如果您有非常量输入,则它们来自何处(内存、寄存器、最近的计算,您可能首先在向量寄存器中执行这些计算?)以及您对结果向量所做的操作都很重要。将单独的标量变量混入/移出SIMD向量有点糟糕,需要在ALU端口瓶颈与存储/重新加载的延迟和吞吐量(以及标量->向量的存储转发暂停)之间进行权衡。不过,在asm中,存储/重新加载对于从SIMD向量中获取大量小元素非常有用,因为您确实需要它们
对于常量
C_a
和C_b
,即使是MSVC也能通过\u mm_集
在常量传播方面做得很好。因此,编写特定于实现的初始值设定项(如
请记住,性能的真正决定因素是可以诱使编译器生成的程序集,而不是用来生成的内部函数。
#include <immintrin.h>
__m128i xor_const(__m128i v) {
return _mm_xor_si128(v, _mm_set_epi64x(0x789abc, 0x123456));
}
我们可以看到,\u mm_set
在静态存储中编译为一个16字节的常量,就像我们想要的那样。未能使用px或xmm0、xmm1是令人惊讶的,但与GCC和/或clang相比。同样,作为一个大函数的一部分,当它有寄存器选择时,我们可能没有额外的movdqa
。如果xor在循环中,那么在循环外加载一次就是我们想要的。这不是MSVC的最新版本;GOODBET只具有最新的MSVC版本,安装在C++中,而不是C,但您标记了C.
相比之下,GCC9.2-O3编译为在所有CPU上都有效的预期内存源PXOR
xor_const:
pxor xmm0, XMMWORD PTR .LC0[rip]
ret
.section .rodata # Godbolt strips out stuff like section directive; re-added manually
.LC0:
.quad 1193046
.quad 7903932
您可能会让编译器发出相同的asm,其中包含一个保持常量的静态数组
alignas(16)
array,并从中加载\u mm\u load\u si128()
。但是为什么要麻烦呢
要避免的一件事是写入
静态常量m128i C=\u mm\u集…
-编译器对此非常愚蠢,不会将\u mm\u集
折叠到\u m128i
的静态常量初始值设定项中。C编译器将拒绝编译非常量静态初始值设定项。C++编译器将保留一些BSS空间,运行一个类似于构造函数的函数,从只读常量复制到该BSS空间,因为<>代码> MyMyStuts在这种情况下不能完全优化。 < P>这纯粹是关于常数<代码> C<代码>的情况。如果您有非常量输入,则它们来自何处(内存、寄存器、最近的计算,您可能首先在向量寄存器中执行这些计算?)以及您对结果向量所做的操作都很重要。将单独的标量变量混入/移出SIMD向量有点糟糕,需要在ALU端口瓶颈与存储/重新加载的延迟和吞吐量(以及标量->向量的存储转发暂停)之间进行权衡。不过,在asm中,存储/重新加载对于从SIMD向量中获取大量小元素非常有用,因为您确实需要它们
对于常量
C_a
和C_b
,即使是MSVC也能通过\u mm_集
在常量传播方面做得很好。因此,编写特定于实现的初始值设定项(如
请记住,性能的真正决定因素是可以诱使编译器生成的程序集,而不是用来生成的内部函数。
#include <immintrin.h>
__m128i xor_const(__m128i v) {
return _mm_xor_si128(v, _mm_set_epi64x(0x789abc, 0x123456));
}
我们可以看到,\u mm_set
在静态存储中编译为一个16字节的常量,就像我们想要的那样。未能使用px或xmm0、xmm1是令人惊讶的,但与GCC和/或clang相比。同样,作为一个大函数的一部分,当它有寄存器选择时,我们可能没有额外的movdqa
。如果xor在循环中,那么在循环外加载一次就是我们想要的。这不是MSVC的最新版本;GOODBET只具有最新的MSVC版本,安装在C++中,而不是C,但您标记了C.
相比之下,GCC9.2-O3编译为在所有CPU上都有效的预期内存源PXOR
xor_const:
pxor xmm0, XMMWORD PTR .LC0[rip]
ret
.section .rodata # Godbolt strips out stuff like section directive; re-added manually
.LC0:
.quad 1193046
.quad 7903932
您可能会让编译器发出相同的asm,其中包含一个保持常量的静态数组
alignas(16)
array,并从中加载\u mm\u load\u si128()
。但是为什么要麻烦呢
要避免的一件事是写入静态常量m128i C=\u mm\u集…
-编译器对此非常愚蠢,不会将\u mm\u集
折叠到\u m128i
的静态常量初始值设定项中。C编译器将拒绝编译非常量静态初始值设定项。C++编译器将保留一些BSS空间,运行一个类似于构造函数的函数,从只读常量复制到该BSS空间,因为<>代码> MyMyStuts在这种情况下不能完全优化。谢谢,这里有很多很棒的信息,我现在仍然在更详细地介绍这一切。看起来,在当前的实现中,我从代码段中的一个常量(cs:xmmword…
)获得了一个movdqa
)。我要走了