2012-06-29 10 views
5

Sto cercando di ottimizzare il mio codice usando Neon intrinsics. Ho una rotazione a 24 bit su un array a 128 bit (8 ogni uint16_t).Rotazione a 128 bit usando ARM Neon intrinsec

Ecco il mio codice 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); 
} 

Ho controllato la documentazione gcc su Neon Intrinsics e non ha istruzioni per rotazioni vettoriali. Inoltre, ho provato a farlo utilizzando vshlq_n_u16(temp, 8) ma tutti i bit spostati all'esterno di una parola uint16_t vengono persi.

Come ottenere ciò utilizzando gli intrinsechi al neon? A proposito, c'è una documentazione migliore su GCC Neon Intrinsics?

+0

'' armcc' ha __ror' intrinseca – ouah

+0

Cosa succede ad usare l'assembly inline con l'istruzione 'ROR' ARM? – ouah

+0

Preferisco evitare il montaggio. Dal modo in cui sto usando GCC quindi nessun armcc! – Kami

risposta

6

Dopo qualche lettura su Arm Community Blogs, ho trovato questo:

Neon Arm Bitwise Rotation

VEXT: Estratto VEXT estrae un nuovo vettore di byte da un paio di vettori esistenti. I byte nel nuovo vettore provengono dalla parte superiore del primo operando e dalla parte inferiore del secondo operando. Ciò consente di produrre un nuovo vettore contenente elementi che si trovano a cavallo di una coppia di vettori esistenti. VEXT può essere utilizzato per implementare una finestra mobile su dati da due vettori, utile nei filtri FIR. Per la permutazione, può anche essere utilizzato per simulare un'operazione di rotazione in byte, quando si utilizza lo stesso vettore per entrambi gli operandi di input.

Il seguente Neon GCC intrinseca fa la stessa cosa come il montaggio fornito nella foto:

uint16x8_t vextq_u16 (uint16x8_t, uint16x8_t, const int) 

Così la rotazione 24bit su un vettore a 128bit completo (non più di ogni elemento) potrebbe essere fatto da il seguente:

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 meno che non manchi qualcosa, questo è troppo complicato rispetto a 'vextq_u8' per eseguire l'intera rotazione in un'unica istruzione. –

4

Non sono sicuro al 100% ma non penso che NEON abbia ruotare le istruzioni.

è possibile comporre l'operazione di rotazione si richiede con uno spostamento a sinistra, un cazzo a destra e una o, per es .:

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

Basta fare lo stesso con gli intrinseci al neon per spostamento a sinistra, merda a destra eo.

uint16x8_t temp; 
uint8_t rot; 

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

Vedi http://en.wikipedia.org/wiki/Circular_shift "Implementazione turni circolari."

Questo ruoterà i valori all'interno delle corsie. Se si desidera ruotare le corsie, utilizzare VEXT come descritto nell'altra risposta.

+0

Non sto chiedendo come fare una rotazione circolare in c! Sto chiedendo come farlo usando Neon Intrinsics! – Kami

+0

OK, ho aggiunto le chiamate intrinseche effettive. –

+0

Questo è meno male che la risposta del PO (3 istruzioni invece di 5), ma a meno che 'vext.8' è molto lento rispetto alle istruzioni di byte-shift, è ancora inefficiente. –

2

Usa vext.8 per concat un vettore con se stesso e ti danno la finestra a 16 byte che si desidera (in questo compensato da 3 byte caso).

Fare questo con intrinseche requires casting per mantenere il compilatore felice, ma è ancora una singola istruzione:

#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 questo:

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

Un conteggio di 16- 3 significa che ruotiamo a sinistra di 3 byte. (Significa che prendiamo 13 byte dal vettore di sinistra e 3 byte dal vettore di destra, quindi è anche una rotazione di destra di 13).


correlati: x86 ha inoltre istruzioni che prende una finestra scorrevole nella concatenazione di due registri: palignr (aggiunto SSSE3).


forse mi manca qualcosa su NEON, ma io non capisco perché auto-risposta del PO sta usando vext.16 (vextq_u16), che ha 16 bit di granularità. Non è nemmeno un'istruzione diversa, solo un alias per vext.8 che rende impossibile l'utilizzo di un conteggio dispari, che richiede istruzioni aggiuntive. The manual for vext.8 says:

VEXT pseudo-istruzione

È possibile specificare un tipo di dati 16, 32, o 64 anziché 8. In questo caso , #imm riferisce a halfword, parole, o doubleword anziché facendo riferimento ai byte, e gli intervalli consentiti sono corrispondentemente ridotti.

Problemi correlati