2010-07-13 10 views
13

come utilizzare l'intrinseco Moltiplica-Accumulo fornito da GCC?Come utilizzare la moltiplicazione e l'accumulo di elementi intrinseci in ARM Cortex-a8?

float32x4_t vmlaq_f32 (float32x4_t , float32x4_t , float32x4_t); 

Qualcuno può spiegare quello che tre parametri devo passare a questa funzione. Intendo i registri di origine e destinazione e cosa restituisce la funzione?

Aiuto !!!

+6

I documenti GCC (e la documentazione RealView per gli elementi intrinseci su cui gli elementi intrinseci del GCC sembrano essere basati su) sono piuttosto scarsi ... se non si ottiene una risposta decente, suggerirei di compilarne una poche chiamate e dare un'occhiata all'assemblea che è uscita. Questo dovrebbe darti una buona idea (anche se è un modo meno che ideale per andare). –

risposta

19

detto semplicemente le istruzioni vmla esegue le seguenti operazioni:

struct 
{ 
    float val[4]; 
} float32x4_t 


float32x4_t vmla (float32x4_t a, float32x4_t b, float32x4_t c) 
{ 
    float32x4 result; 

    for (int i=0; i<4; i++) 
    { 
    result.val[i] = b.val[i]*c.val[i]+a.val[i]; 
    } 

    return result; 
} 

E tutto questo compila in un'istruzione singe assembler :-)

È possibile utilizzare questo NEON-assembler intrinseco tra altre cose in tipiche moltiplicazioni di matrice 4x4 per grafica 3D come questa:

float32x4_t transform (float32x4_t * matrix, float32x4_t vector) 
{ 
    /* in a perfect world this code would compile into just four instructions */ 
    float32x4_t result; 

    result = vml (matrix[0], vector); 
    result = vmla (result, matrix[1], vector); 
    result = vmla (result, matrix[2], vector); 
    result = vmla (result, matrix[3], vector); 

    return result; 
} 

Ciò consente di risparmiare un paio di cicli perché non è necessario aggiungere i risultati dopo la moltiplicazione. L'aggiunta è così spesso usata che moltiplica-accumula hsa diventa mainstream in questi giorni (anche x86 li ha aggiunti in qualche recente set di istruzioni SSE).

Vale anche la pena ricordare: operazioni di accumulo multipla come questa sono molto comuni in algebra lineare e applicazioni DSP (digital signal processing). ARM è stato molto intelligente e ha implementato il percorso rapido all'interno del NEON-Core Cortex-A8. Questo percorso veloce viene attivato se il primo argomento (l'accumulatore) di un'istruzione VMLA è il risultato di una precedente istruzione VML o VMLA. Potrei entrare nei dettagli ma in poche parole una serie di istruzioni è quattro volte più veloce di una serie VML/VADD/VML/VADD.

Dai un'occhiata al mio semplice moltiplicatore di matrice: ho fatto esattamente questo. A causa di questo percorso rapido verrà eseguito circa quattro volte più velocemente rispetto all'implementazione scritta utilizzando VML e ADD invece di VMLA.

+0

Grazie per la risposta così dettagliata. La tua risposta non spiega solo la funzionalità delle istruzioni, ma anche i pro e i contro per l'utilizzo di queste istruzioni. – HaggarTheHorrible

+0

Ciao Nils, ho capito come la moltiplicazione della matrice può essere accelerata usando le istruzioni NEON. È davvero avvincente ora :) Voglio usare le istruzioni NEON per fare l'inverso di una matrice, puoi indicarmi alcuni buoni documenti che spiegano come usare le istruzioni NEON per fare l'inverso di una matrice o puoi darmi qualche idea, come fare questo? Grazie. – HaggarTheHorrible

+1

per la matrice inversa Vorrei fare una ricerca su google "sse matrix inverse" e portare il codice sse su NEON. Il modo usuale è calcolare l'inverso per le piccole matrici (4x4) tramite la regola di Cramers. –

8

Google per vmlaq_f32, visualizzato the reference for the RVCT compiler tools. Ecco cosa dice:

Vector multiply accumulate: vmla -> Vr[i] := Va[i] + Vb[i] * Vc[i] 
... 
float32x4_t vmlaq_f32 (float32x4_t a, float32x4_t b, float32x4_t c); 

E

i seguenti tipi sono definiti per rappresentare vettori. I tipi di dati vettore neon sono denominati in base al seguente schema: <tipo> <dimensioni> x < numero di corsie > _t Ad esempio, int16x4_t è un vettore contenente quattro corsie ciascuna contenente una firma interi a 16 bit. La Tabella E.1 elenca i tipi di dati vettoriali.

IOW, il valore restituito dalla funzione sarà un vettore contenente 4 carri a 32 bit, e ciascun elemento del vettore viene calcolata moltiplicando i corrispondenti elementi b e c, e aggiungendo il contenuto del a.

HTH

1
result = vml (matrix[0], vector); 
result = vmla (result, matrix[1], vector); 
result = vmla (result, matrix[2], vector); 
result = vmla (result, matrix[3], vector); 

Questa sequenza non funziona, però.Il problema è che componente x accumula solo X modulata dalle righe di matrice e può essere espressa come:

result.x = vector.x * (matrix[0][0] + matrix[1][0] + matrix[2][0] + matrix[3][0]); 

...

la sequenza corretta sarebbe:

result = vml (matrix[0], vector.xxxx); 
result = vmla(result, matrix[1], vector.yyyy); 

...

NEON e SSE non dispongono di selezione integrata per i campi (ciò richiederebbe 8 bit nell'istruzione che incodifica, per registro vettoriale). Ad esempio, GLSL/HLSL dispone di questo tipo di servizi, così come la maggior parte delle GPU.

modo alternativo per raggiungere questo sarebbe:

result.x = dp4(vector, matrix[0]); 
result.y = dp4(vector, matrix[1]); 

... // e naturalmente, la matrice sarebbe trasporre per questo cedere stesso risultato

Il mul, madd, madd, la sequenza di madd è solitamente preferita in quanto non richiede la maschera di scrittura per i campi del registro di destinazione.

In caso contrario, il codice sembra buono. =)