2011-05-26 23 views
6

Necesito comparar dos almacenamientos intermedios para la igualdad. No necesito información sobre la relación de los dos almacenamientos intermedios, solo si cada dos fragmentos son iguales o no. Mi máquina Intel soporta hasta SSE4.2comparar almacenamientos intermedios tan rápido como sea posible

El enfoque ingenuo es:

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

comparación con mi primer intento usando SSE nunca:

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

La ganancia de velocidad es aproximadamente un 33%. ¿Podría hacerlo mejor?

+0

¿Tiene un cuello de botella en este código en particular? –

+0

Sí, es el principal punto caliente en mi programa. – beutelfuchs

+0

A menos que su implementación 'memcmpy' esté rota, tendrá dificultades para superarla, ya debería estar optimizada SIMD. –

Respuesta

4

realidad, no debería ser el uso de código escalar y los sindicatos para poner a prueba todos los elementos vectoriales individuales - hacer algo como esto en su lugar:

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 ??? 
} 
+0

Gracias, ese es exactamente el tipo de información que estoy buscando – beutelfuchs

+0

Pero, por desgracia, la ganancia es la misma en comparación con el uso de la unión para este caso especial. – beutelfuchs

+0

Puede ser que su cuello de botella sea el ancho de banda de la memoria, por lo que acelerar las operaciones de comparación podría ser infructuoso. –

2

Ya que se puede utilizar SSE 4.1, hay otra alternativa que podría ser más rápido:

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) vuelve 0 si a & b != 0 y vuelve 1 si a & b == 0. La ventaja es que también puede usar esta versión con las nuevas instrucciones AVX, donde el tamaño del fragmento es de 32 bytes.

+0

Gracias. Lamentablemente, VS2005 no es compatible con SSE por encima de v2, como aprendí en ese momento. Tal vez pueda averiguar los códigos de operación y usar asm en línea en su lugar. – beutelfuchs

+0

Intenté usar el compilador de inteligencia que soporta estos instrintos.El rendimiento es comparable a los otros enfoques presentados aquí. – beutelfuchs

+0

Gracias por probar el código. Es bastante probable que el ancho de banda de la memoria sea el cuello de botella aquí, como señaló Paul R. Este código podría ser potencialmente 1 o 2 ciclos más rápido. –

Cuestiones relacionadas