2013-04-02 17 views
7

Stiamo eseguendo un programma scientifico e vorremmo implementare le funzionalità AVX. L'intero programma (scritto in Fortran + C) sta per essere vettorializzato e al momento sto cercando di implementare la moltiplicazione di numeri complessi all'interno dell'assemblaggio in linea di GCC.Codice assembly/istruzioni AVX per la moltiplicazione di numeri complessi. (Assemblaggio in linea GCC)

Il codice del montaggio 4 numeri complessi ed esegue due moltiplicazioni complesse contemporaneamente:

v2complex cmult(v2complex *a, v2complex *b) { 
    v2complex ret; 
    asm (
     "vmovupd %2,%%ymm1;" 
     "vmovupd %2, %%ymm2;" 
     "vmovddup %%ymm2, %%ymm2;" 
     "vshufpd $15,%%ymm1,%%ymm1,%%ymm1;" 
     "vmulpd %1, %%ymm2, %%ymm2;" 
     "vmulpd %1, %%ymm1, %%ymm1;" 
     "vshufpd $5,%%ymm1,%%ymm1, %%ymm1;" 
     "vaddsubpd %%ymm1, %%ymm2,%%ymm1;" 
     "vmovupd %%ymm1, %0;" 
     : 
     "=m"(ret) 
     : 
     "m" (*a), 
     "m" (*b) 
     ); 
    return ret; 
} 

dove A e B sono 256 bit doppia precisione:

typedef union v2complex { 
    __m256d v; 
    complex c[2]; 
} v2complex; 

Il problema è che il il codice produce principalmente il risultato corretto, ma a volte fallisce.

Sono molto nuovo all'assemblaggio, ma ho cercato di capirlo da solo. Sembra che il programma C (ottimizzato -O3) interagisca con i registri ymm utilizzati nel codice assembly. Ad esempio, posso stampare uno dei valori (ad es. A) prima di eseguire la moltiplicazione e il programma non dà mai risultati errati.

La mia domanda è come dire a GCC di non interagire con ymm. Non sono riuscito a inserirenell'elenco di registri con errori.

risposta

7

Come si suppone, il problema è che non hai detto al GCC che ti registra che stai sbavando. Sono sorpreso se non supportano ancora la creazione di registri YMM nella lista dei clobber; quale versione di GCC stai usando?

In ogni caso, sarà quasi certamente sufficiente a mettere i registri XMM corrispondenti nell'elenco clobber invece:

: "=m" (ret) : "m" (*a), "m" (*b) : "%xmm1", "%xmm2"); 

Alcune altre note:

  • si sta caricando sia input due volte, che è inefficiente. Non c'è motivo per farlo.
  • Vorrei usare "r" (a), "r" (b) come vincoli e scrivere i miei carichi come vmovupd (%2), %%ymm1. Probabilmente nessuna differenza nel codice generato, ma sembra più idiomaticamente corretta.
  • Non dimenticare di inserire un codice vzeroupper seguendo il codice AVX prima di eseguire qualsiasi codice SSE per evitare (grandi) bancarelle.
+0

grazie mille, che ha risolto il problema =). Sto usando gcc 4.7.2 e thx per il tuo consiglio. –

+1

Non usare '" r "(a)," r "(b)' con 'vmovupd (% 2), %% ymm1' ecc. GCC presumerà che * a e * b non siano accessibili (a meno che tu non aggiungi un clobber di "memoria"). –

3

aggiungo due commenti, non direttamente rispondere alla tua domanda:

  • vi consiglio vivamente di utilizzare intrinseci compilatore invece di montaggio diretto. In questo modo il compilatore si occupa dell'allocazione del registro e può fare un lavoro migliore per ottimizzare il codice (metodi in linea, istruzioni di riordino, ecc.)
  • Agner Fog ha un C++ vector class library di operazioni vettoriali ottimizzate, comprese le operazioni su numeri complessi. Anche se potresti non essere in grado di usare le sue librerie direttamente nel tuo codice C, il suo codice ottimizzato potrebbe essere un buon punto di partenza; vedi src/special/complexvec.h in the zipped source code.
+0

Thx, lo darò un'occhiata anche se non sarò in grado di usarlo. In realtà, avevo compilato entrambe le versioni, il codice intrinseco e l'assembly, e volevo sapere perché l'assembly non funzionava bene poiché entrambi sono stati compilati con lo stesso codice assembly ottimizzato (objdump -S ..). –

+0

Ma gli intrinseci hanno anche degli svantaggi: il compilatore si occupa dell'allocazione dei registri e delle istruzioni di riordino, ecc. Non è generalmente vero che un compilatore lo fa meglio. –