对于我正在研究的业余爱好项目,我需要在x86 CPU上模拟某些64位整数运算,并且它需要 fast 。
目前,我正在通过MMX指令执行此操作,但这真的很难用,因为我必须一直刷新fp寄存器状态(并且因为大多数MMX指令处理签名整数,我需要无符号行为。)
所以我想知道SO上的SSE /优化专家是否可以使用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;
具体来说,我不需要通用添加或移位,例如,只需添加一个和左移一个。真的,只是这里显示的完全操作。
当然,除了x86之外,使用两个32位标量来模拟uint64_t
,这很慢(而且,在我的情况下,根本不起作用,因为我需要加载/存储原子,它们在加载/存储两个独立的寄存器时不会出现。)
因此,我需要一个SIMD解决方案。
其中一些操作很简单,已经由SSE2支持。其他人(!=
和<
)需要更多的工作。
连连呢? SSE和SSE2都没问题。允许SSE3需要一些说服力,SSE4可能是不可能的(支持SSE4的CPU可能无论如何都要运行64位,因此我不需要这些解决方法)
答案 0 :(得分:14)
SSE2直接支持某些64位整数运算:
将两个元素都设置为0:
__m128i z = _mm_setzero_si128();
将两个元素都设置为1:
__m128i z = _mm_set_epi32(0,1,0,1);
垂直添加/减去每个64位整数:
__m128i z = _mm_add_epi64(x,y)
__m128i z = _mm_sub_epi64(x,y)
左移:
__m128i z = _mm_slli_epi64(x,i) // i must be an immediate
按位运算符:
__m128i z = _mm_and_si128(x,y)
__m128i z = _mm_or_si128(x,y)
SSE没有增量,因此您必须使用1
的常量。
比较更难,因为没有64位支持。
这是平等的:
__m128i t = _mm_cmpeq_epi32(a,b);
__m128i z = _mm_and_si128(t,_mm_shuffle_epi32(t,177));
如果它们相等,这将把每个64位元素设置为0xffffffffffff
。如果您希望0
或1
中的int
或_mm_cvtsi32_si128()
,则可以使用1
将其删除并添加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);
。
并且小于:(未完全测试)
0xffffffffffff
如果a
中的相应元素小于b
,则会将每个64位元素设置为inline bool equals(__m128i a,__m128i b){
__m128i t = _mm_cmpeq_epi32(a,b);
__m128i z = _mm_and_si128(t,_mm_shuffle_epi32(t,177));
return _mm_cvtsi128_si32(z) & 1;
}
inline bool lessthan(__m128i a,__m128i b){
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);
return _mm_cvtsi128_si32(z) & 1;
}
。
以下是返回bool的“equals”和“less-than”的版本。它们返回底部64位整数的比较结果。
{{1}}