SSE优化了64位整数的仿真

jal*_*alf 9 c++ optimization 64-bit x86 sse

对于我正在开发的业余爱好项目,我需要在x86 CPU上模拟某些64位整数运算,并且它需要很快.

目前,我正在通过MMX指令这样做,但这真的很难用,因为我必须一直刷新fp寄存器状态(因为大多数MMX指令处理有符号整数,我需要无符号行为).

所以我想知道如果SSE /优化大师在这里SO可以拿出使用SSE更好地执行.

我需要的操作如下(非常具体):

uint64_t X, Y;

X = 0;
X = 1;
X << 1;
X != Y;
X + 1;
X & 0x1 // get lsb
X | 0x1 // set lsb
X > Y;
Run Code Online (Sandbox Code Playgroud)

具体来说,我不需要通用添加或移位,例如,只需添加一个和左移一个.真的,只是这里显示的确切操作.

当然,除了在x86上,uint64_t使用两个32位标量进行模拟,这很慢(在我的情况下,根本不起作用,因为我需要加载/存储是原子的,它们不会是当加载/存储两个单独的寄存器时).

因此,我需要一个SIMD解决方案.其中一些操作很简单,已经由SSE2支持.其他人(!=<)需要更多的工作.

建议?SSE和SSE2都没问题.允许SSE3需要一些说服力,SSE4可能是不可能的(支持SSE4的CPU 无论如何都可能运行64位,所以我不需要这些解决方法)

Mys*_*ial 15

SSE2直接支持某些64位整数运算:

将两个元素都设置为0:

__m128i z = _mm_setzero_si128();
Run Code Online (Sandbox Code Playgroud)

将两个元素都设置为1:

__m128i z = _mm_set1_epi64x(1);      // also works for variables.
__m128i z = _mm_set_epi64x(hi, lo);  // elements can be different

__m128i z = _mm_set_epi32(0,1,0,1);  // if any compilers refuse int64_t in 32-bit mode.  (None of the major ones do.)
Run Code Online (Sandbox Code Playgroud)

垂直添加/减去每个64位整数:

// supported even in 32-bit mode, and listed as an intrinsic for MOVQ
// so it should be atomic on aligned integers.
_mm_loadl_epi64((const __m128i*)p);     // movq or movsd 64-bit load

_mm_cvtsi64x_si128(a);      // only ICC, others refuse in 32-bit mode
_mm_loadl_epi64((const __m128i*)&a);  // portable for a value instead of pointer
Run Code Online (Sandbox Code Playgroud)

http://software.intel.com/sites/products/documentation/studio/composer/en-us/2011/compiler_c/intref_cls/common/intref_sse2_integer_arithmetic.htm#intref_sse2_integer_arithmetic

左移:

__m128i z = _mm_add_epi64(x,y)
__m128i z = _mm_sub_epi64(x,y)
Run Code Online (Sandbox Code Playgroud)

http://software.intel.com/sites/products/documentation/studio/composer/en-us/2011/compiler_c/intref_cls/common/intref_sse2_int_shift.htm

按位运算符:

__m128i z = _mm_slli_epi64(x,i)   // i must be an immediate
Run Code Online (Sandbox Code Playgroud)

http://software.intel.com/sites/products/documentation/studio/composer/en-us/2011/compiler_c/intref_cls/common/intref_sse2_integer_logical.htm

SSE没有增量,所以你必须使用常量_mm_set_epi32.


比较更难,因为没有64位支持.

这是平等的:

__m128i z = _mm_and_si128(x,y)
__m128i z = _mm_or_si128(x,y)
Run Code Online (Sandbox Code Playgroud)

_mm_loadl_epi64如果它们相等,这将设置每个64位元素.如果你想把它作为一个1pcmpeqq一个pcmpgtq,你可以使用0xffffffffffff和添加它-1).

和小于:(未完全测试)

__m128i t = _mm_cmpeq_epi32(a,b);
__m128i z = _mm_and_si128(t,_mm_shuffle_epi32(t,177));
Run Code Online (Sandbox Code Playgroud)

0如果相应的元素in 1小于,则将每个64位元素设置为int.


这是"等于"和"小于"的版本,返回一个bool.它们返回底部64位整数的比较结果.

a = _mm_xor_si128(a,_mm_set1_epi32(0x80000000));
b = _mm_xor_si128(b,_mm_set1_epi32(0x80000000));
__m128i t = _mm_cmplt_epi32(a,b);
__m128i u = _mm_cmpgt_epi32(a,b);
__m128i z = _mm_or_si128(t,_mm_shuffle_epi32(t,177));
z = _mm_andnot_si128(_mm_shuffle_epi32(u,245),z);
Run Code Online (Sandbox Code Playgroud)