2015-03-27 12 views
11

I collegamenti introduttive ho trovato durante la ricerca:Come vettorializzare il mio ciclo con g ++?

  1. 6.59.14 Loop-Specific Pragmas
  2. 2.100 Pragma Loop_Optimize
  3. How to give hint to gcc about loop count
  4. Tell gcc to specifically unroll a loop
  5. How to Force Vectorization in C++

Come si può vedere la maggior parte di loro sono per C, ma ciò ho anche pensato che potessero funzionare anche in C++. Qui è il mio codice:

template<typename T> 
//__attribute__((optimize("unroll-loops"))) 
//__attribute__ ((pure)) 
void foo(std::vector<T> &p1, size_t start, 
      size_t end, const std::vector<T> &p2) { 
    typename std::vector<T>::const_iterator it2 = p2.begin(); 
    //#pragma simd 
    //#pragma omp parallel for 
    //#pragma GCC ivdep Unroll Vector 
    for (size_t i = start; i < end; ++i, ++it2) { 
    p1[i] = p1[i] - *it2; 
    p1[i] += 1; 
    } 
} 

int main() 
{ 
    size_t n; 
    double x,y; 
    n = 12800000; 
    vector<double> v,u; 
    for(size_t i=0; i<n; ++i) { 
     x = i; 
     y = i - 1; 
     v.push_back(x); 
     u.push_back(y); 
    } 
    using namespace std::chrono; 

    high_resolution_clock::time_point t1 = high_resolution_clock::now(); 
    foo(v,0,n,u); 
    high_resolution_clock::time_point t2 = high_resolution_clock::now(); 

    duration<double> time_span = duration_cast<duration<double>>(t2 - t1); 

    std::cout << "It took me " << time_span.count() << " seconds."; 
    std::cout << std::endl; 
    return 0; 
} 

ho usato al i suggerimenti si possono vedere commentati in precedenza, ma non ho avuto alcun aumento di velocità, come una mostra di uscita del campione (con la prima esecuzione dopo aver commentata questa #pragma GCC ivdep Unroll Vector:

[email protected]:~/Downloads$ g++ test.cpp -O3 -std=c++0x -funroll-loops -ftree-vectorize -o test 
[email protected]:~/Downloads$ ./test 
It took me 0.026575 seconds. 
[email protected]:~/Downloads$ g++ test.cpp -O3 -std=c++0x -o test 
[email protected]:~/Downloads$ ./test 
It took me 0.0252697 seconds. 

c'è qualche speranza o il flag di ottimizzazione O3 appena fa il trucco qualsiasi suggerimento per velocizzare questo codice (la funzione foo) sono i benvenuti

la mia versione di g ++:?!

[email protected]:~/Downloads$ g++ --version 
g++ (Ubuntu 4.8.1-2ubuntu1~12.04) 4.8.1 

Si noti che il corpo del ciclo è casuale. Non sono interessante nel riscriverlo in qualche altra forma.


EDIT

Una risposta dicendo che non c'è nulla di più che si può fare è accettabile!

+0

Così hai guardato all'assemblaggio per vedere se è già vettorizzato sotto '-O3'? – Mysticial

+0

Oh accidenti, no non l'ho fatto. Lo farò, controllando questa domanda: http://stackoverflow.com/questions/1289881/using-gcc-to-produce-readable-assembly Buona idea @Mysticial! – gsamaras

+0

@Mysticial forse la risposta data da David non rende necessaria la lettura dell'assemblea? – gsamaras

risposta

9

Il flag O3 si attiva con -linea automaticamente vettoriale. https://gcc.gnu.org/onlinedocs/gcc/Optimize-Options.html

-O3 accende tutte le ottimizzazioni specificati da -O2 ed anche accende i -finline-funzioni, -funswitch-loop, -fpredictive-commoning, -fgcse-dopo-ricarica, -ftree-loop-vectorize ,, -ftree-SLP-vectorize, costo-modello -fvect, -ftree-parziali pre e opzioni -fipa-cp-clone

Quindi, in entrambi i casi il compilatore modelli -ftree-loop-distribute- sta cercando di fare la vettorizzazione del ciclo.

Utilizzo di g ++ 4.8.2 di compilare con:

g++ test.cpp -O2 -std=c++0x -funroll-loops -ftree-vectorize -ftree-vectorizer-verbose=1 -o test 

dà questo:

Analyzing loop at test.cpp:16                                                            


Vectorizing loop at test.cpp:16                                                            

test.cpp:16: note: create runtime check for data references *it2$_M_current_106 and *_39                                              
test.cpp:16: note: created 1 versioning for alias checks.                                                     

test.cpp:16: note: LOOP VECTORIZED.                                                           
Analyzing loop at test_old.cpp:29                                                            

test.cpp:22: note: vectorized 1 loops in function.                                                       

test.cpp:18: note: Unroll loop 7 times                                                          

test.cpp:16: note: Unroll loop 7 times                                                          

test.cpp:28: note: Unroll loop 1 times 

compilazione senza il flag -ftree-vectorize: solo

g++ test.cpp -O2 -std=c++0x -funroll-loops -ftree-vectorizer-verbose=1 -o test 

restituisce questo:

test_old.cpp:16: note: Unroll loop 7 times 

test_old.cpp:28: note: Unroll loop 1 times 

Linea 16 è il inizio della funzione del ciclo n, quindi il compilatore lo sta definitivamente vettorializzando. Il controllo dell'assemblatore conferma anche questo.

Mi sembra di avere un po 'di cache aggressiva sul laptop che sto usando, il che rende molto difficile misurare con precisione quanto tempo impiega la funzione per funzionare.

Ma qui sono un paio di altre cose che si possono provare anche:

  • Usare il qualificatore __restrict__ per dire al compilatore che non v'è sovrapposizione tra le matrici.

  • dire al compilatore gli array sono allineati con __builtin_assume_aligned (non portatile)

Ecco il mio codice risultante (ho tolto il modello dal momento che si desidera utilizzare l'allineamento diverso per i diversi tipi di dati)

#include <iostream> 
#include <chrono> 
#include <vector> 

void foo(double * __restrict__ p1, 
      double * __restrict__ p2, 
      size_t start, 
      size_t end) 
{ 
    double* pA1 = static_cast<double*>(__builtin_assume_aligned(p1, 16)); 
    double* pA2 = static_cast<double*>(__builtin_assume_aligned(p2, 16)); 

    for (size_t i = start; i < end; ++i) 
    { 
     pA1[i] = pA1[i] - pA2[i]; 
     pA1[i] += 1; 
    } 
} 

int main() 
{ 
    size_t n; 
    double x, y; 
    n = 12800000; 
    std::vector<double> v,u; 

    for(size_t i=0; i<n; ++i) { 
     x = i; 
     y = i - 1; 
     v.push_back(x); 
     u.push_back(y); 
    } 

    using namespace std::chrono; 

    high_resolution_clock::time_point t1 = high_resolution_clock::now(); 
    foo(&v[0], &u[0], 0, n); 
    high_resolution_clock::time_point t2 = high_resolution_clock::now(); 

    duration<double> time_span = duration_cast<duration<double>>(t2 - t1); 

    std::cout << "It took me " << time_span.count() << " seconds."; 
    std::cout << std::endl; 

    return 0; 
} 

Come ho già detto, ho avuto problemi a ottenere misurazioni di tempo coerenti, quindi non posso confermare se questo ti darà un aumento delle prestazioni (o forse addirittura diminuirà!)

+0

Nessuna differenza! Forse il '-unroll-loops' è già abilitato da O2, ma non ho potuto confermarlo. Se hai altri suggerimenti, utilizza il pulsante di modifica (consigliato: D). – gsamaras

+0

Sì, ci ho provato e non ho fatto alcuna differenza, lasciami provare alcune cose e vedere cosa riesco a trovare :) –

+0

Se non hai nulla di nuovo, potrei accettare la risposta forse, ma devi farmelo sapere! – gsamaras

1

GCC ha estensioni al compilatore che crea nuove primitive che utilizzeranno le istruzioni SIMD. Dai un'occhiata a here per i dettagli.

La maggior parte dei compilatori dice che eseguiranno operazioni di vettorizzazione automatica, ma ciò dipende dalla corrispondenza del modello del compilatore, ma come immaginate questo può essere molto approssimativo.

+0

Interessante, ma non sono sicuro di quale dimensione devo passare nell'attributo, puoi guidarmi? – gsamaras

+0

Penso che molte architetture abbiano registri SIMD a 128 bit, quindi tieni tutti gli oggetti a 128 bit di larghezza. Ricorda inoltre che SIMD non accelera il carico di dati e le ore di archiviazione, ma accelera le operazioni aritmetiche. – doron

+0

Ancora non è chiaro come dovrei farlo. Sono limitato a 4 dimensioni? Un esempio potrebbe aiutare. – gsamaras

Problemi correlati