2010-10-21 11 views
6

Tengo la siguiente función de cuello de botella.¿Cómo optimizar un ciclo?

typedef unsigned char byte; 
void CompareArrays(const byte * p1Start, const byte * p1End, const byte * p2, byte * p3) 
{ 
    const byte b1 = 128-30; 
    const byte b2 = 128+30; 
    for (const byte * p1 = p1Start; p1 != p1End; ++p1, ++p2, ++p3) { 
     *p3 = (*p1 < *p2) ? b1 : b2; 
    } 
} 

quiero reemplazar C++ código con SSE2 funciones intinsic. He intentado _mm_cmpgt_epi8 pero utilizó la comparación firmada. Necesito una comparación sin firma.

¿Hay algún truco (SSE, SSE2, SSSE3) para resolver mi problema?

Nota: No quiero usar multi-threading en este caso.

+0

¿Sabe a qué arquitectura de procesador se dirige? Trabajar con un fragmento de palabra de 64 bits a la vez (un poco alborotado para hacer comparaciones en el registro) podría reducir algo la contención del bus de memoria. El código de ensamblado del compilador debería ayudar a proporcionar ideas ... ... ¿y no está destinado a SSE para operaciones de coma flotante, no enteros? –

+0

SSE tiene algunas instrucciones enteras. – Crashworks

+1

¿Por qué no los hizo firmados? un XOR simple 0x80 con cada elemento antes de la comparación hará el trabajo. – ruslik

Respuesta

9

En lugar de compensar sus valores con signo para hacerlos sin signo, de una manera un poco más eficiente sería hacer lo siguiente:

  • uso _mm_min_epu8 para obtener el min sin signo de p1, p2
  • comparar este min por la igualdad con p2 usando _mm_cmpeq_epi8
  • la máscara resultante será ahora 0x00 para los elementos que P1 y P2 < 0xff para los elementos donde p1> p2 =
  • ahora puede usar esta máscara con _mm_or_si128 y _mm_andc_si128 para seleccionar los valores B1/B2 apropiadas

Tenga en cuenta que este es de 4 instrucciones en total, en comparación con 5 usando el método de comparación de desplazamiento + firmado.

1

Sí, la SSE no funcionará aquí. Puede mejorar este rendimiento del código en el ordenador multi-núcleo mediante el uso de OpenMP:

 
void CompareArrays(const byte * p1Start, const byte * p1End, const byte * p2, byte * p3) 
{ 
    const byte b1 = 128-30; 
    const byte b2 = 128+30; 

    int n = p1End - p1Start; 
    #pragma omp parallel for 
    for (int i = 0; i < n; ++p1, ++i) 
    { 
     p3[i] = (p1[i] < p2[i]) ? b1 : b2; 
    } 
} 
+3

Esto no funcionará para un solo núcleo o CPU única –

+0

@ VJo - sí, por supuesto. En la computadora de núcleo único, este código funciona exactamente como el código original de la pregunta. –

+1

@VJo funcionará, pero no dará ningún impulso – Andrey

-3

uso pcmpeqb y ser el poder contigo.

+1

'pcmpeqb' es un cheque por igual. Necesito menos comparar. –

+0

ah sí. luego pcmpgtb. todavía usa el poder. pero sabiamente –

+2

OP necesita una comparación sin firmar. –

2

Puede restar 127 de sus números, y luego usar _mm_cmpgt_epi8

+2

Parece la respuesta correcta. Pero creo que su 127 debe reemplazarse por 128. O xor con 128. –

+0

El problema es que creo que solo hay un agregado empaquetado en MMX, que es un conjunto de registros diferente. – Crashworks

+0

sí, tienes razón. 128, no 127 –

2

Sí, esto se puede hacer en SIMD, pero va a tomar unos pasos para hacer la máscara.

Ruslik lo hizo bien, creo. Desea xor cada componente con 0x80 para voltear el sentido de la comparación firmada y sin firmar. _mm_xor_si128 (PXOR) lo consigue; necesitará crear la máscara como una matriz de caracteres estática en alguna parte antes de cargarla en un registro SIMD. Entonces _mm_cmpgt_epi8 te da una máscara y puedes usar un Y a nivel de bit (ej. _mm_and_si128) para realizar un movimiento enmascarado.

-1

Desafortunadamente, muchas de las respuestas anteriores son incorrectas. Supongamos una palabra de 3 bits:

unsigned: 4 5 6 7 0 1 2 3 == signed: -4 -3 -2 -1 0 1 2 3 (bits: 100 101 110 111 000 001 010 011)

El método de Paul R es incorrecto. Supongamos que queremos saber si 3> 2. min (3,2) == 2, lo que sugiere que sí, por lo que el método funciona aquí. Ahora supongamos que queremos saber si 7> 2. El valor 7 es -1 en la representación con signo, por lo que min (-1,2) == -1, lo que sugiere erróneamente que 7 no es mayor que 2 sin signo.

El método de Andrey también es incorrecto. Supongamos que queremos saber si 7> 2, o a = 7, y b = 2. El valor 7 es -1 en representación firmada, por lo que el primer término (a> b) falla, y el método sugiere que 7 no es mayor que 2

Sin embargo, el método de BJobnh, corregido por Alexey, es correcto. Simplemente resta 2^(n-1) de los valores, donde n es el número de bits. En este caso, restaríamos 4 para obtener los nuevos valores correspondientes:

viejo firmado: -4 -3 -2 -1 0 1 2 3 => nuevo firmado: 0 1 2 3 -4 -3 -2 -1 == nuevo sin firmar 0 1 2 3 4 5 6 7.

En otras palabras, unsigned_greater_than (a, b) es equivalente a signed_greater_than (a - 2^(n-1), b - 2^(n-1)).

+0

Si miras detenidamente mi respuesta, verás que estoy usando una operación * sin signo * mínimo. –