Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

compare buffers as fast as possible

Tags:

c

windows

sse

I need to compare two buffers chunk-wise for equality. I don't need information about the relation of the two buffers, just if each two chunks are equal or not. My intel machine supports up to SSE4.2

The naive approach is:

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;
}

Compared to my first try using SSE ever:

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;
}

The gain in speed is about 33%. Could I do any better?

like image 701
beutelfuchs Avatar asked May 26 '11 09:05

beutelfuchs


1 Answers

You really shouldn't be using scalar code and unions to test all the individual vector elements - do something like this instead:

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 ???
}
like image 158
Paul R Avatar answered Sep 24 '22 12:09

Paul R