2013-03-11 5 views
6

Sto cercando di implementare alcuni assembler inline (in Visual Studio 2012 codice C++) per sfruttare SSE. Voglio aggiungere 7 numeri per 1e9 volte, quindi li ho posizionati dalla RAM ai registri xmm0 a xmm6 della CPU. quando lo faccio con assembly inline in Visual Studio 2012 con questo codice:L'utilizzo del registro XMM0 e dei recuperi di memoria (codice C++) è due volte più veloce dell'ASM usando solo registri XMM - Perché?

il codice C++:

for(int i=0;i<count;i++) 
     resVal+=val1+val2+val3+val4+val5+val6+val7; 

mio codice ASM:

int count=1000000000; 

    double resVal=0.0; 
     //placing values to register 
    __asm{ 
     movsd xmm0,val1;placing var1 in xmm0 register 
     movsd xmm1,val2 
     movsd xmm2,val3 
     movsd xmm3,val4 
     movsd xmm4,val5 
     movsd xmm5,val6 
     movsd xmm6,val7 
     pxor xmm7,xmm7;//turns xmm7 to zero 
     } 

    for(int i=0;i<count;i++) 
    { 
     __asm 
     { 
      addsd xmm7,xmm0;//+=var1 
      addsd xmm7,xmm1;//+=var2 
      addsd xmm7,xmm2; 
      addsd xmm7,xmm3; 
      addsd xmm7,xmm4; 
      addsd xmm7,xmm5; 
      addsd xmm7,xmm6;//+=var7 
     } 

    } 

    __asm 
     { 
      movsd resVal,xmm7;//placing xmm7 into resVal 
     } 

e questo è il dis assemblati codice C++ compilatore per il codice 'resVal + = val1 val2 + + + val3 val4 + val5 + Val6 + val7':

movsd  xmm0,mmword ptr [val1] 
addsd  xmm0,mmword ptr [val2] 
addsd  xmm0,mmword ptr [val3] 
addsd  xmm0,mmword ptr [val4] 
addsd  xmm0,mmword ptr [val5] 
addsd  xmm0,mmword ptr [val6] 
addsd  xmm0,mmword ptr [val7] 
addsd  xmm0,mmword ptr [resVal] 
movsd  mmword ptr [resVal],xmm0 

Come è visibile la cOMPI usa un solo registro xmm0 e per altre volte sta recuperando i valori dalla RAM.

La risposta di entrambi i codici (il mio codice ASM e il codice C++) è la stessa ma il codice C++ impiega circa la metà del tempo del mio codice asm da eseguire!

Mi è stato letto sui registri della CPU che lavorare con loro è molto più veloce della memoria. Non penso che questo rapporto sia vero. Perché la versione asm ha prestazioni inferiori del codice C++?

+0

Perché stai facendo questo? È strettamente un esercizio di apprendimento o hai qualcosa che vuoi raggiungere? –

+1

@Bwmat: il compilatore ottiene il valore dalla RAM ogni volta e lo fa per 1e9 volte, ma potrebbe essere un vantaggio posizionare valori nei registri della CPU e ottenerli per 1e9 volte! – epsi1on

+0

Sono leggermente sorpreso che il compilatore non abbia ottimizzato l'intero 'val1 + val2 + val3 + val4 + val5 + val6 + val7' e lo sostituisca con un singolo valore. – Mysticial

risposta

10
  • Una volta che i dati sono nella cache (che sarà il caso dopo il primo ciclo, se non c'è già), fa poca differenza se si utilizza la memoria o la registrazione.
  • Un'aggiunta in virgola mobile richiede un po 'più tempo rispetto al ciclo singolo.
  • Il negozio finale su resVal "slega" il registro xmm0 per consentire al registro di essere liberamente "rinominato", che consente di eseguire più loop in parallelo.

Questo è un tipico caso di "se non si è assolutamente sicuri, lasciare codice di scrittura al compilatore".

L'ultimo punto in alto spiega perché il codice è più veloce del codice in cui ogni fase del ciclo dipende da un risultato calcolato in precedenza.

Nel codice compilatore ha generato, il ciclo può fare l'equivalente di:

movsd  xmm0,mmword ptr [val1] 
addsd  xmm0,mmword ptr [val2] 
addsd  xmm0,mmword ptr [val3] 
addsd  xmm0,mmword ptr [val4] 
addsd  xmm0,mmword ptr [val5] 
addsd  xmm0,mmword ptr [val6] 
addsd  xmm0,mmword ptr [val7] 
addsd  xmm0,mmword ptr [resVal] 
movsd  mmword ptr [resVal],xmm0 

movsd  xmm1,mmword ptr [val1] 
addsd  xmm1,mmword ptr [val2] 
addsd  xmm1,mmword ptr [val3] 
addsd  xmm1,mmword ptr [val4] 
addsd  xmm1,mmword ptr [val5] 
addsd  xmm1,mmword ptr [val6] 
addsd  xmm1,mmword ptr [val7] 
addsd  xmm1,mmword ptr [resVal] 
movsd  mmword ptr [resVal],xmm1 

Ora, come potete vedere, siamo riusciti a "mescolarsi" questi due "fili":

movsd  xmm0,mmword ptr [val1] 
movsd  xmm1,mmword ptr [val1] 
addsd  xmm0,mmword ptr [val2] 
addsd  xmm1,mmword ptr [val2] 
addsd  xmm0,mmword ptr [val3] 
addsd  xmm1,mmword ptr [val3] 
addsd  xmm0,mmword ptr [val4] 
addsd  xmm1,mmword ptr [val4] 
addsd  xmm0,mmword ptr [val5] 
addsd  xmm1,mmword ptr [val5] 
addsd  xmm0,mmword ptr [val6] 
addsd  xmm1,mmword ptr [val6] 
addsd  xmm0,mmword ptr [val7] 
addsd  xmm1,mmword ptr [val7] 
addsd  xmm0,mmword ptr [resVal] 
movsd  mmword ptr [resVal],xmm0 
// Here we have to wait for resval to be uppdated! 
addsd  xmm1,mmword ptr [resVal] 
movsd  mmword ptr [resVal],xmm1 

Non sto suggerendo che si tratti di un'esecuzione fuori dal comune, ma posso certamente vedere come il ciclo può essere eseguito più velocemente del tuo ciclo. Probabilmente puoi ottenere la stessa cosa nel tuo codice assembler se hai un registro di riserva [in x86_64 hai altri 8 registri, anche se non puoi usare l'assemblatore inline in x86_64 ...]

(nota che la rinomina del registro è diversa dal mio ciclo "threaded", che utilizza due registri diversi, ma l'effetto è più o meno lo stesso, il ciclo può continuare dopo aver eseguito l'aggiornamento "resVal" senza dover attendere per il risultato da aggiornare)

+1

puoi spiegare perché il codice C++ funziona due volte più velocemente? – epsi1on

+1

Ho rotto la tua risposta nei proiettili. Ma non sono sicuro di quanto sia applicabile il secondo proiettile. L'ultima parte relativa alla ridenominazione e alla rottura della dipendenza, credo, è la risposta corretta. – Mysticial

+2

@ epsi1on Il codice assembly ha una catena di dipendenze dei dati su 'xmm7'.Il codice generato dal compilatore non lo fa perché tutti i negozi di 'xmm0' sono archivi morti. – Mysticial

0

Potrebbe essere utile per te non utilizzare _asm, ma le funzioni intrinseche e i tipi di instrinsic come __m128i di __m128d witch rappresentano i registri di sse. Vedi immintrin.h è definire i tipi e molte funzioni di sse. puoi trovare una buona descrizione e le specifiche per loro qui: http://software.intel.com/sites/landingpage/IntrinsicsGuide/

Problemi correlati