2012-06-08 15 views
14

Questa è la prima volta che lavoro con gli intrinseca SSE. Sto cercando di convertire un semplice pezzo di codice in una versione più veloce utilizzando Intel SSE intrinseco (fino a SSE4.2). Mi sembra di incontrare un numero di errori.Ottimizzazione del codice tramite Intel SSE intrinseca per la vettorizzazione

La versione scalare del codice è: (semplice moltiplicazione di matrici)

 void mm(int n, double *A, double *B, double *C) 
    { 
     int i,j,k; 
     double tmp; 

     for(i = 0; i < n; i++) 
      for(j = 0; j < n; j++) { 
        tmp = 0.0; 
        for(k = 0; k < n; k++) 
          tmp += A[n*i+k] * 
            B[n*k+j]; 
        C[n*i+j] = tmp; 

       } 
      } 

Questa è la mia versione: Ho incluso #include

 void mm_sse(int n, double *A, double *B, double *C) 
     { 
     int i,j,k; 
     double tmp; 
     __m128d a_i, b_i, c_i; 

     for(i = 0; i < n; i++) 
      for(j = 0; j < n; j++) { 
        tmp = 0.0; 
        for(k = 0; k < n; k+=4) 
          a_i = __mm_load_ps(&A[n*i+k]); 
          b_i = __mm_load_ps(&B[n*k+j]); 
          c_i = __mm_load_ps(&C[n*i+j]); 

          __m128d tmp1 = __mm_mul_ps(a_i,b_i); 
          __m128d tmp2 = __mm_hadd_ps(tmp1,tmp1); 
          __m128d tmp3 = __mm_add_ps(tmp2,tmp3); 
          __mm_store_ps(&C[n*i+j], tmp3); 

      } 
     } 

Dove sto andando di sbagliato in questo? Sto ottenendo diversi errori come questo:

mm_vec.c (84): errore: un valore di tipo "int" non può essere assegnato ad un'entità di tipo "__m128d" A_i = __mm_load_ps (& A [n * i + k]);

questo è come mi sto la compilazione: ICC -O2 mm_vec.c -o vec

qualcuno può aiutare me convertire questo codice in modo accurato. Grazie!

UPDATE:

secondo i vostri suggerimenti, mi hanno fatto le seguenti modifiche:

 void mm_sse(int n, float *A, float *B, float *C) 
     { 
     int i,j,k; 
     float tmp; 
     __m128 a_i, b_i, c_i; 

     for(i = 0; i < n; i++) 
      for(j = 0; j < n; j++) { 
        tmp = 0.0; 
        for(k = 0; k < n; k+=4) 
          a_i = _mm_load_ps(&A[n*i+k]); 
          b_i = _mm_load_ps(&B[n*k+j]); 
          c_i = _mm_load_ps(&C[n*i+j]); 

          __m128 tmp1 = _mm_mul_ps(a_i,b_i); 
          __m128 tmp2 = _mm_hadd_ps(tmp1,tmp1); 
          __m128 tmp3 = _mm_add_ps(tmp2,tmp3); 
          _mm_store_ps(&C[n*i+j], tmp3); 


      } 
     } 

Ma ora mi sembra di essere sempre un errore di segmentazione. Lo so forse perché non sto accedendo correttamente agli indici di array per gli array A, B, C. Sono molto nuovo a questo e non sono sicuro di come procedere con questo.

Si prega di aiutarmi a determinare l'approccio corretto verso la gestione di questo codice.

risposta

9

L'errore che stai vedendo è perché avete troppe sottolineature nei nomi di funzione, ad esempio:

__mm_mul_ps 

dovrebbe essere:

_mm_mul_ps // Just one underscore up front 

in modo che il compilatore C sta assumendo ritornano int dal momento che non ha visto una dichiarazione.

Oltre a ciò, ci sono ulteriori problemi: sembra che tu stia missando chiamate a varianti float doppie e singole della stessa istruzione.

Per esempio si dispone di:

__m128d a_i, b_i, c_i; 

ma si chiama:

__mm_load_ps(&A[n*i+k]); 

che restituisce un non __m128 un __m128d - si voleva chiamare:

_mm_load_pd 

invece. Allo stesso modo per le altre istruzioni se vuoi che funzionino su coppie di doppi.


se stai vedendo i difetti di segmentazione inspiegabili e nel codice SSE sarei propenso a immaginare che hai problemi di allineamento di memoria - puntatori passati alla intrinseche SSE (per lo più) devono essere 16 byte allineato. Puoi inserire check this with a simple assert nel tuo codice o controllarlo in un debugger (ti aspetti che l'ultima cifra del puntatore sia 0 se è allineata correttamente).

Se non è allineato a destra è necessario assicurarsi che sia. Per le cose non allocati con new/malloc() si può fare questo con un'estensione del compilatore (ad es with gcc):

float a[16] __attribute__ ((aligned (16))); 

fornito la propria versione di gcc ha un allineamento max grande abbastanza per sostenere questo e pochi altri avvertimenti circa l'allineamento dello stack . Per l'archiviazione allocata dinamicamente, ti consigliamo di utilizzare un'estensione specifica della piattaforma, ad es. posix_memalign per assegnare memoria adatto:

float *a=NULL; 
posix_memalign(&a, __alignof__(__m128), sizeof(float)*16); 

(penso che ci potrebbe essere più bello, modi portatili di fare questo con C++ 11, ma io non sono sicuro al 100% su quello ancora).

Esistono alcune istruzioni che consentono di eseguire carichi e negozi non allineati, ma sono terribilmente lenti rispetto ai carichi allineati e vale la pena di evitarli se possibile.

+0

Sto lavorando con ICC non GCC. Pensi che gestirlo in questo modo: a_i = _mm_load_ps (& A [n * i + k]); è l'approccio corretto? Gli esempi che vedo pubblicati altrove (anche sulla documentazione Intel Intrinsic) hanno esempi molto semplici. le matrici A, B, C sono state tutte allocate con malloc. – PGOnTheGo

+1

@Hello_PG Il carico non è direttamente sbagliato. Non è comunque necessario caricare c_i. Per la maggior parte ICC ha le stesse estensioni di gcc - penso che sia il caso di quello di allineamento, ho più familiarità con GCC che con ICC personalmente, quindi mi sono qualificato con quello e ho collegato i documenti che sapevo come trovare. malloc non garantisce l'allineamento adatto su tutte le piattaforme, quindi posix_memalign è probabilmente necessario. L'affermazione che ho suggerito fallire? – Flexo

+0

Quando provo ad allocare memoria per A in questo modo: A = (float *) _ aligned_malloc (dimensione * dimensione * sizeof (float), 16); Ricevo un errore di compilazione: riferimento indefinito a 'aligned_malloc 'con icc. questo è come sto la compilazione: ICC -O2 mm_vec.c -o vec2 – PGOnTheGo

3

È necessario fare in modo che i carichi ei negozi sono sempre accedono indirizzi allineati 16 byte. In alternativa, se non si può garantire questo, per qualche motivo, quindi utilizzare _mm_loadu_ps/_mm_storeu_ps invece di _mm_load_ps/_mm_store_ps - questo sarà meno efficiente, ma non andrà in crash su indirizzi non allineati.