Estoy vectorizando un fragmento de código y en algún momento tengo la siguiente configuración:
register m128 a = { 99,99,99,99,99,99,99,99 } register m128 b = { 100,50,119,30,99,40,50,20 } Actualmente estoy empaquetando s short en estos registros, por lo que tengo 8 valores por registro. Lo que me gustaría hacer es restar el i-ésimo elemento en b con el valor correspondiente en a si el i-ésimo valor de b es mayor o igual que el valor en a (En este caso, a se llena con la constante 99 ). Con este fin, primero uso una operación mayor o igual que entre b y a , lo que produce, para este ejemplo:
register m128 c = { 1,0,1,0,1,0,0,0 } Para completar la operación, me gustaría usar multiplicar y restar, es decir, almacenar en b la operación b -= a*c . El resultado sería entonces:
b = { 1,50,20,30,0,40,50,20 } ¿Hay alguna operación que haga tal cosa? Lo que encontré fueron operaciones fusionadas para Haswell, pero actualmente estoy trabajando en Sandy-Bridge. Además, si alguien tiene una mejor idea para hacer esto, hágamelo saber (por ejemplo, podría hacer una resta lógica: si 1 en c entonces resto, nada de lo contrario.
Básicamente, desea una versión SSE de este código, ¿verdad?
if (b >= a) t = ba else t = b b = tDado que queremos evitar condicionales para la versión SSE, podemos deshacernos del flujo de control de esta manera (tenga en cuenta que la máscara está invertida):
uint16_t mask = (b>=a)-1 uint16_t tmp = ba; uint16_t d = (b & mask) | (tmp & ~mask) b = d Revisé el intrínseco _mm_cmpgt_epi16 y tiene una buena propiedad que devuelve 0x0000 para falso o 0xFFFF para verdadero, en lugar de un solo bit 0 o 1 (eliminando así la necesidad de la primera resta). Por lo tanto, nuestra versión SSE podría verse así.
__m128i mask = _mm_cmpgt_epi16 (b, a) __m128i tmp = _mm_sub_epi16 (b, a) __m128 d = _mm_or_ps (_mm_and_ps (mask, tmp), _mm_andnot_ps (mask, b))EDITAR : Harold ha mencionado una respuesta mucho menos complicada. La solución anterior puede ser útil si necesita modificar la parte else de if/else .
uint16_t mask = ~( (b>=a)-1 ) uint16_t tmp = a & mask b = b - tmpel código SSE será
__m128i mask = _mm_cmpgt_epi16 (b, a) __m128i t = _mm_sub_epi16 (b, _mm_and_si128 (mask, a))Otra alternativa, si sus entradas no están firmadas, puede calcular
b = min(b, ba); Esto funciona, porque si a>b entonces ba se ajusta y se garantiza que dará como resultado un valor mayor que b . Para a<=b siempre obtendrá un valor entre 0 y b inclusive.
b = _mm_min_epu16(b, _mm_sub_epi16(b,a)); El _mm_min_epu16 requerido requiere SSE4.1 o posterior ( _mm_min_epu8 solo requeriría SSE2).
Puede copiar b a c , restar a de c , realizar un desplazamiento aritmético de 15 posiciones a la derecha en los valores de 16 bits, complementar el valor de c , enmascarar c con a y finalmente restar c de b .
No estoy familiarizado con la sintaxis intrínseca, pero los pasos son:
register m128 c = b; c -= a; c >>= 15; c = ~c; c &= a; b -= c;aquí hay una alternativa con menos pasos:
register m128 c = compare_ge(b, a); c = -c; c &= a; b -= c;