сравнить буферы как можно быстрее

мне нужно сравнить два буфера по куску для равенства. Мне не нужна информация об отношении двух буферов, только если каждые два куска равны или нет. Моя машина intel поддерживает до 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%. Могу ли я сделать лучше?

2 ответов


вы действительно не должны использовать скалярный код и объединения для тестирования всех отдельных элементов вектора - сделайте что-то вроде этого:

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

_mm_testz_si128(a, b) возвращает 0 Если a & b != 0 и это возвращает 1 если a & b == 0. Преимущество заключается в том, что вы можете использовать эту версию с новыми инструкциями AVX, где размер куска составляет 32 байта.