C++ 如何在C++;03给定它是不透明类型和数组?
交换C++ 如何在C++;03给定它是不透明类型和数组?,c++,swap,intrinsics,c++03,sunstudio,C++,Swap,Intrinsics,C++03,Sunstudio,交换变量的最佳实践是什么 背景是一个C++03编译器\uuuu m128i是一种不透明类型,与MMX和SSE指令一起使用,它通常是和无符号long-long[2]。C++03不支持交换数组,并且std:swap(\uuuu m128i a,\uuuu m128i b)在编译器下失败 以下是一些相关的问题,但它们并不十分中肯。它们不适用,因为std::vector不可用 通过memcpy进行交换 #include <emmintrin.h> #include <cstr
变量的最佳实践是什么
背景是一个C++03编译器\uuuu m128i
是一种不透明类型,与MMX和SSE指令一起使用,它通常是和无符号long-long[2]
。C++03不支持交换数组,并且std:swap(\uuuu m128i a,\uuuu m128i b)
在编译器下失败
以下是一些相关的问题,但它们并不十分中肯。它们不适用,因为std::vector
不可用
通过
memcpy进行交换
#include <emmintrin.h>
#include <cstring>
template<class T>
void memswap(T& a, T& b)
{
T t;
std::memcpy(&t, &a, sizeof(t));
std::memcpy(&a, &b, sizeof(t));
std::memcpy(&b, &t, sizeof(t));
}
int main() {
__m128i x;
__m128i y;
memswap(x, y);
return 0;
}
#包括
#包括
模板
无效成员交换(T&a、T&b)
{
T;
std::memcpy(&t,&a,sizeof(t));
std::memcpy(&a,&b,sizeof(t));
std::memcpy(&b,&t,sizeof(t));
}
int main(){
__m128ix;
__m128i-y;
memswap(x,y);
返回0;
}
这听起来不像是最佳实践问题;听起来您需要一个解决办法来解决严重破坏的intrinsic实现。如果\uuuM128i tmp=a代码>不编译,这很糟糕
如果要编写自定义交换函数,请保持简单<代码>\uuum128i
是一种POD类型,适合于单个向量寄存器。不要做任何会鼓励编译器将其溢出到内存中的事情。有些编译器甚至会为一个微不足道的测试用例生成非常糟糕的代码,甚至gcc/clang也可能会在优化一个复杂的大函数时被memcpy绊倒
由于编译器阻塞了构造函数,只需使用普通的初始值设定项声明一个tmp变量,并使用=
赋值进行复制。这在任何支持\uuuu m128i
的编译器中都是有效的,并且是一种常见的模式
对内存中的值进行简单赋值或从中赋值的工作方式类似于\u mm\u store\u si128
/\u mm\u load\u si128
:即,如果在未对齐的地址上使用,则会出现故障。(当然,优化可能会导致加载被折叠到另一条向量指令的内存操作数中,或者根本不会发生存储。)
使用memswap,您可以得到如下结果
return1st_memcpy(__m128i, __m128i): ## ICC13 -O3
movdqa XMMWORD PTR [-56+rsp], xmm0
movdqa XMMWORD PTR [-40+rsp], xmm1 # spill both
movaps xmm2, XMMWORD PTR [-56+rsp] # reload x
movaps XMMWORD PTR [-24+rsp], xmm2 # copy x to tmp
movaps xmm0, XMMWORD PTR [-40+rsp] # reload y
movaps XMMWORD PTR [-56+rsp], xmm0 # copy y to x
movaps xmm0, XMMWORD PTR [-24+rsp] # reload tmp
movaps XMMWORD PTR [-40+rsp], xmm0 # copy tmp to y
movdqa xmm0, XMMWORD PTR [-40+rsp] # reload y
ret # return y
这几乎是您可以想象的交换两个寄存器的溢出/重新加载的绝对最大量,因为icc13根本不会在内联memcpy
s之间进行优化,甚至不会记住寄存器中剩下的内容
交换内存中已有的值
甚至gcc也会使memcpy版本的代码变得更糟糕。它使用64位整数加载/存储而不是128位向量加载/存储进行复制。如果您将要加载向量(存储转发暂停),那么这是非常糟糕的,否则就是糟糕的(更多UOP来做同样的工作)
如果要编写自定义交换函数,只需使用按值赋值,因为这是\uuum128i
值的最佳选择。gcc确实能够优化memcpy并将值保留在寄存器中(使用一个测试函数,该函数接受两个\uuuum128i
参数并返回一个\uuuuum128i
)。简单的分配不太可能对优化产生负面影响。例如,ICC13没有问题。如果两个值都已在内存中,则在\uuu m128i
上使用memswap
时,即使是gcc也可能出现故障。看看我的答案。谢谢彼得,这就是我一直在寻找的洞察力。最后一个悬而未决的问题:我应该提供一个,还是干脆把它作为一个独立的函数?@jww:最好只调用它vecswap
,这样您就可以在派生类型上使用它了。(例如)。如果您是专门从事此项工作的,您可能希望为\uuuum128i
,\uuuuum128d
,\uuuum128
,\uuuuum256*
,\uuuuum512*
,以及其他体系结构的任何SIMD类型进行此项工作。(手臂霓虹灯或w/e),然后它就相当笨重了。我更乐意使用一个不同的函数,我相信它总是很轻,并且优化得很好。如果你确定你只需要几个类型,那么值得考虑专门化。信不信由你,这导致了SunCC下最初的编译问题:T=a;a=b;b=t代码>。我必须T;t=a,a=b,b=t让编译器停止尝试初始化它。@jww:Wow,编译器会被大量代码阻塞。我假设它阻塞在\uuum128i v=\umm\uadd\uepi32(a,b)代码>也是吗?对于大多数代码,我认为我只是考虑它被破坏了,而不是重写任何重要的代码以获得它的好处。@彼得科德如何<代码> VECWAsP()/<代码>不同于<代码> STD::SWAP-()/COD>?
__m128i test_return2nd(__m128i x, __m128i y) {
vecswap(x, y);
return x;
}
movdqa xmm0, xmm1
ret # returning the 2nd arg, which was in xmm1
__m128i test_return1st(__m128i x, __m128i y) {
vecswap(x, y);
return y;
}
ret # returning the first arg, already in xmm0
return1st_memcpy(__m128i, __m128i): ## ICC13 -O3
movdqa XMMWORD PTR [-56+rsp], xmm0
movdqa XMMWORD PTR [-40+rsp], xmm1 # spill both
movaps xmm2, XMMWORD PTR [-56+rsp] # reload x
movaps XMMWORD PTR [-24+rsp], xmm2 # copy x to tmp
movaps xmm0, XMMWORD PTR [-40+rsp] # reload y
movaps XMMWORD PTR [-56+rsp], xmm0 # copy y to x
movaps xmm0, XMMWORD PTR [-24+rsp] # reload tmp
movaps XMMWORD PTR [-40+rsp], xmm0 # copy tmp to y
movdqa xmm0, XMMWORD PTR [-40+rsp] # reload y
ret # return y
// the memcpy version of this compiles badly
void test_mem(__m128i *x, __m128i *y) {
vecswap(*x, *y);
}
# gcc 5.3 and ICC13 make the same code here, since it's easy to optimize
movdqa xmm0, XMMWORD PTR [rdi]
movdqa xmm1, XMMWORD PTR [rsi]
movaps XMMWORD PTR [rdi], xmm1
movaps XMMWORD PTR [rsi], xmm0
ret
// gcc 5.3 with memswap instead of vecswap. ICC13 is similar
test_mem_memcpy(long long __vector(2)*, long long __vector(2)*):
mov rax, QWORD PTR [rdi]
mov rdx, QWORD PTR [rdi+8]
mov r9, QWORD PTR [rsi]
mov r10, QWORD PTR [rsi+8]
mov QWORD PTR [rdi], r9
mov QWORD PTR [rdi+8], r10
mov QWORD PTR [rsi], rax
mov QWORD PTR [rsi+8], rdx
ret