问题描述
|
我需要逐块比较两个缓冲区的相等性。我不需要有关两个缓冲区的关系的信息,即使每两个块相等还是不相等。我的英特尔机器最多支持SSE4.2
天真的方法是:
const size_t CHUNK_SIZE = 16; //128bit for SSE2 integer registers
const int ARRAY_SIZE = 200000000;
char* array_1 = (char*)_aligned_malloc(ARRAY_SIZE,16);
char* array_2 = (char*)_aligned_malloc(ARRAY_SIZE,16);
for (size_t i = 0; i < ARRAY_SIZE; )
{
volatile bool result = memcmp(array_1+i,array_2+i,CHUNK_SIZE);
i += CHUNK_SIZE;
}
与我第一次尝试使用SSE相比:
union U
{
__m128i m;
volatile int i[4];
} res;
for (size_t i = 0; i < ARRAY_SIZE; )
{
__m128i* pa1 = (__m128i*)(array_1+i);
__m128i* pa2 = (__m128i*)(array_2+i);
res.m = _mm_cmpeq_epi32(*pa1,*pa2);
volatile bool result = ( (res.i[0]==0) || (res.i[1]==0) || (res.i[2]==0) || (res.i[3]==0) );
i += CHUNK_SIZE;
}
速度的提高约为33%。我能做得更好吗?
解决方法
您实际上不应该使用标量代码和并集来测试所有单个矢量元素-而是执行以下操作:
for (size_t i = 0; i < ARRAY_SIZE; i += CHUNK_SIZE)
{
const __m128i a1 = _mm_load_si128(array_1 + i);
const __m128i a2 = _mm_load_si128(array_2 + i);
const __m128i vcmp = _mm_cmpeq_epi32(a1,a2);
const int vmask = _mm_movemask_epi8(vcmp);
const bool result = (vmask == 0xffff);
// you probably want to break here if you get a mismatch ???
}
,由于可以使用SSE 4.1,因此还有另一种可能更快的选择:
for (size_t i = 0; i < ARRAY_SIZE; i += CHUNK_SIZE;)
{
__m128i* pa1 = (__m128i*)(array_1+i);
__m128i* pa2 = (__m128i*)(array_2+i);
__m128i temp = _mm_xor_si128(*pa1,*pa2);
bool result = (bool)_mm_testz_si128(temp,temp);
}
如果a & b != 0
,4ѭ返回0
,如果a & b == 0
,则返回1
。优点是您也可以将此版本与新的AVX指令一起使用,其中块大小为32字节。