2012-06-29 16 views
5

Estoy tratando de optimizar mi código utilizando los intrínsecos de Neon. Tengo una rotación de 24 bits en una matriz de 128 bits (8 cada uint16_t).Rotación de 128 bits utilizando intrínsecos de ARM Neon

Aquí está mi código C:

uint16_t rotated[8]; 
uint16_t temp[8]; 
uint16_t j; 
for(j = 0; j < 8; j++) 
{ 
    //Rotation <<< 24 over 128 bits (x << shift) | (x >> (16 - shift) 
    rotated[j] = ((temp[(j+1) % 8] << 8) & 0xffff) | ((temp[(j+2) % 8] >> 8) & 0x00ff); 
} 

He comprobado la documentación sobre gcc Neon Intrinsics y no tiene instrucciones para rotaciones de vector. Además, he intentado hacer esto usando vshlq_n_u16(temp, 8) pero todos los bits cambiados fuera de una palabra uint16_t se pierden.

¿Cómo lograr esto usando intrínsecos de neón? Por cierto, ¿hay una mejor documentación sobre GCC Neon Intrinsics?

+0

'' armcc' ha __ror' intrínseca – ouah

+0

¿Qué pasa con el uso de ensamblado en línea con la instrucción 'ROR' ARM? – ouah

+0

Prefiero evitar el ensamblaje. Por cierto estoy usando GCC así que no armcc! – Kami

Respuesta

6

Después de leer un poco sobre Arm Community Blogs, he encontrado esto:

Neon Arm Bitwise Rotation

VEXT: Extracto VEXT extrae un nuevo vector de bytes de un par de vectores existentes. Los bytes en el nuevo vector son desde la parte superior del primer operando, y la parte inferior del segundo operando. Esto le permite producir un nuevo vector que contiene elementos que forman un par de vectores existentes. VEXT se puede utilizar para implementar una ventana móvil en los datos de dos vectores, útil en filtros FIR. Para la permutación, también se puede usar para simular una operación de rotación de byte, cuando se usa el mismo vector para ambos operandos de entrada.

La siguiente Neon GCC intrínseca hace lo mismo que el conjunto proporcionado en la imagen:

uint16x8_t vextq_u16 (uint16x8_t, uint16x8_t, const int) 

Así que el de la rotación de 24 bits sobre un vector de 128 bits completo (no más de cada elemento) podría ser hecho por lo siguiente:

uint16x8_t input; 
uint16x8_t t0; 
uint16x8_t t1; 
uint16x8_t rotated; 

t0 = vextq_u16(input, input, 1); 
t0 = vshlq_n_u16(t0, 8); 
t1 = vextq_u16(input, input, 2); 
t1 = vshrq_n_u16(t1, 8); 
rotated = vorrq_u16(t0, t1); 
+0

A menos que me falta algo, esto es demasiado complicado en comparación con 'vextq_u8' para hacer toda la rotación en una instrucción. –

4

No estoy 100% seguro, pero no creo que NEON haya rotado las instrucciones.

podrá componer la operación de rotación se requiere con una desviación a la izquierda, una mierda derecho y una o, por ejemplo .:

uint8_t ror(uint8_t in, int rotation) 
{ 
    return (in >> rotation) | (in << (8-rotation)); 
} 

Sólo hacer lo mismo con las características intrínsecas de neón por un desplazamiento a la izquierda, derecha de la mierda y o.

uint16x8_t temp; 
uint8_t rot; 

uint16x8_t rotated = vorrq_u16 (vshlq_n_u16(temp, rot) , vshrq_n_u16(temp, 16 - rot)); 

Ver http://en.wikipedia.org/wiki/Circular_shift "Aplicación de desplazamientos circulares."

Esto rotará los valores dentro de los carriles. Si desea rotar los carriles, use VEXT como se describe en la otra respuesta.

+0

¡No estoy preguntando cómo hacer una rotación circular en c! ¡Estoy preguntando cómo hacerlo usando Neon Intrinsics! – Kami

+0

OK, he agregado las llamadas intrínsecas reales. –

+0

Esto es menos malo que la respuesta del OP (3 instrucciones en lugar de 5), pero a menos que 'vext.8' sea realmente lento en comparación con las instrucciones de cambio de byte, sigue siendo ineficiente. –

2

uso vext.8 a conCat un vector consigo mismo y le dan la ventana de 16 bytes que desea (en este compensado por 3 bytes caso).

Hacer esto con los intrínsecos requires casting para mantener el compilador feliz, pero sigue siendo una sola instrucción:

#include <arm_neon.h> 

uint16x8_t byterotate3(uint16x8_t input) { 
    uint8x16_t tmp = vreinterpretq_u8_u16(input); 
    uint8x16_t rotated = vextq_u8(tmp, tmp, 16-3); 
    return vreinterpretq_u16_u8(rotated); 
} 

g++5.4 -O3 -march=armv7-a -mfloat-abi=hard -mfpu=neon (on Godbolt) compila a esto:

byterotate3(__simd128_uint16_t): 
    vext.8 q0, q0, q0, #13 
    bx  lr 

Es un recuento de 16- 3 significa que rotamos a la izquierda por 3 bytes. (Significa que tomamos 13 bytes del vector izquierdo y 3 bytes del vector derecho, por lo que también gira 13 a la derecha).


relacionadas: x86 también tiene la instrucción que tiene una ventana deslizante en la concatenación de dos registros: palignr (añadido en SSSE3).


Tal vez me falta algo de neón, pero yo no entiendo por qué la auto-respuesta de la OP está utilizando vext.16 (vextq_u16), que tiene una granularidad de 16 bits. Ni siquiera es una instrucción diferente, solo un alias para vext.8 que hace que sea imposible usar un conteo impar, requiriendo instrucciones adicionales. The manual for vext.8 says:

VEXT pseudo-instrucción

Se puede especificar un tipo de datos de 16, 32, o 64 en lugar de 8. En este caso , #imm se refiere a medias palabras, palabras, o palabras dobles en lugar de refiriéndose a los bytes, y los rangos permitidos son correspondientemente reducido.

Cuestiones relacionadas