2012-07-02 23 views
8

Estoy tratando de convertir un código c en uno optimizado utilizando intrínsecos de neón.Neón equivalente a intrínseca SSE

Aquí están los códigos c que operan sobre 2 operantes no sobre vectores de operantes.

uint16_t mult_z216(uint16_t a,uint16_t b){ 
unsigned int c1 = a*b; 
    if(c1) 
    { 
     int c1h = c1 >> 16; 
     int c1l = c1 & 0xffff; 
     return (c1l - c1h + ((c1l<c1h)?1:0)) & 0xffff; 
    } 
    return (1-a-b) & 0xffff; 
} 

La versión VER optimizada de esta operación ya ha sido implementado por el siguiente texto:

#define MULT_Z216_SSE(a, b, c) \ 
    t0 = _mm_or_si128 ((a), (b)); \ //Computes the bitwise OR of the 128-bit value in a and the 128-bit value in b. 
    (c) = _mm_mullo_epi16 ((a), (b)); \ //low 16-bits of the product of two 16-bit integers 
    (a) = _mm_mulhi_epu16 ((a), (b)); \ //high 16-bits of the product of two 16-bit unsigned integers 
    (b) = _mm_subs_epu16((c), (a)); \ //Subtracts the 8 unsigned 16-bit integers of a from the 8 unsigned 16-bit integers of c and saturates 
    (b) = _mm_cmpeq_epi16 ((b), C_0x0_XMM); \ //Compares the 8 signed or unsigned 16-bit integers in a and the 8 signed or unsigned 16-bit integers in b for equality. (0xFFFF or 0x0) 
    (b) = _mm_srli_epi16 ((b), 15); \ //shift right 16 bits 
    (c) = _mm_sub_epi16 ((c), (a)); \ //Subtracts the 8 signed or unsigned 16-bit integers of b from the 8 signed or unsigned 16-bit integers of a. 
    (a) = _mm_cmpeq_epi16 ((c), C_0x0_XMM); \ ////Compares the 8 signed or unsigned 16-bit integers in a and the 8 signed or unsigned 16-bit integers in b for equality. (0xFFFF or 0x0) 
    (c) = _mm_add_epi16 ((c), (b)); \ // Adds the 8 signed or unsigned 16-bit integers in a to the 8 signed or unsigned 16-bit integers in b. 
    t0 = _mm_and_si128 (t0, (a)); \ //Computes the bitwise AND of the 128-bit value in a and the 128-bit value in b. 
    (c) = _mm_sub_epi16 ((c), t0); ///Subtracts the 8 signed or unsigned 16-bit integers of b from the 8 signed or unsigned 16-bit integers of a. 

casi he convertido éste utilizando las características intrínsecas de neón:

#define MULT_Z216_NEON(a, b, out) \ 
    temp = vorrq_u16 (*a, *b); \ 
    // ?? 
    // ?? 
    *b = vsubq_u16(*out, *a); \ 
    *b = vceqq_u16(*out, vdupq_n_u16(0x0000)); \ 
    *b = vshrq_n_u16(*b, 15); \ 
    *out = vsubq_s16(*out, *a); \ 
    *a = vceqq_s16(*c, vdupq_n_u16(0x0000)); \ 
    *c = vaddq_s16(*c, *b); \ 
    *temp = vandq_u16(*temp, *a); \ 
    *out = vsubq_s16(*out, *a); 

estoy solo falta los equivalentes de neón de _mm_mullo_epi16 ((a), (b)); y _mm_mulhi_epu16 ((a), (b));. O estoy malinterpretando algo o no hay tales intrínsecos en NEON. Si no hay un equivalente de cómo archivar estos pasos usando NEONS intrínsecos?

UPDATE:

He olvidado hacer hincapié en el punto siguiente: los operantes de la función son uint16x8_t vectores NEÓN (cada elemento es un uint16_t => números enteros entre 0 y 65.535). En una respuesta, alguien propuso usar el vqdmulhq_s16() intrínseco. El uso de este no coincidirá con la implementación dada porque la multiplicación intrínseca interpretará los vectores como valores firmados y producirá un resultado incorrecto.

+0

Si tiene valores> 32767, entonces deberá usar la multiplicación de ampliación sugerida a continuación (vmull_u16). Si sabe que sus valores serán todos <32768, entonces puede usar vqdmulhq_s16. – BitBank

Respuesta

5

Se puede utilizar:

uint32x4_t vmull_u16 (uint16x4_t, uint16x4_t) 

que devuelve un vector de productos de 32 bits. Si desea dividir el resultado en partes altas y bajas, puede utilizar NEON para descomprimir intrínsecamente.

+0

Esa instrucción es 16x16 = 32 multiplicar (amplía la salida). Hay instrucciones más cercanas (ver mi respuesta). – BitBank

+1

@BitBank: El OP necesita los 16 bits más altos y los 16 bits inferiores, por lo tanto, necesita un resultado de 32 bits. Una multiplicación de doblar/saturar no es un sustituto porque pierde precisión. –

1

vmulq_s16() es el equivalente de _mm_mullo_epi16. No hay un equivalente exacto de _mm_mulhi_epu16; la instrucción más cercana es vqdmulhq_s16() que es "saturar, duplicar, multiplicar, devolver parte alta". Solo funciona con valores de 16 bits firmados y deberá dividir la entrada o la salida entre 2 para anular la duplicación.

+0

Dado que vqdmulhq_s16() utiliza entradas firmadas, GCC se queja de argumentos tipados incorrectos ... ¿Cómo convertir de manera eficaz de uint16x8_t a int16x8_t? – Kami

+0

Hay macros de lanzamiento; use vreinterpretq_s16_u16() – BitBank

+0

¡Vea mi edición sobre la multiplicación firmada! – Kami