2013-02-14 15 views
6

Capisco che OpenMP è in realtà solo un insieme di macro che è compilato in pthreads. C'è un modo di vedere il codice pthread prima che avvenga il resto della compilation? Sto usando GCC per compilare.Compilare openmp in pthreads Codice C

risposta

8

Primo, OpenMP è non un semplice insieme di macro. Potrebbe essere vista una semplice trasformazione in codice simile a pthread, ma OpenMP richiede più di quello incluso il supporto di runtime.

Torna alla tua domanda, almeno, in GCC, non puoi vedere il codice pthreaded perché l'implementazione di GMP di OpenMP viene eseguita nel back-end del compilatore (o nel middle-end). La trasformazione avviene in livello IR (rappresentazione intermedia). Quindi, dal punto di vista dei programmatori, non è facile vedere come il codice viene effettivamente trasformato.

Tuttavia, ci sono alcuni riferimenti.

(1) Un ingegnere Intel ha fornito una grande panoramica dell'attuazione di OpenMP nel compilatore Intel C/C++:

http://www.drdobbs.com/parallel/how-do-openmp-compilers-work-part-1/226300148

http://www.drdobbs.com/parallel/how-do-openmp-compilers-work-part-2/226300277

(2) Si può prendere uno sguardo alla implementazione di OpenMP di GCC:

https://github.com/mirrors/gcc/tree/master/libgomp

Vedi libgomp.h utilizza pthread e loop.c contiene l'implementazione del costrutto del ciclo parallelo.

-1

Non l'ho provato con openmp. Ma l'opzione del compilatore -E dovrebbe darti il ​​codice dopo la pre-elaborazione.

+0

Non funziona. 'gcc -E' esegue la pre-elaborazione, ma non interpreta' # pragma', che viene molto più avanti nella catena di compilazione. –

5

OpenMP è un insieme di compilatori direttive, non macro. In C/C++ tali direttive sono implementate con il meccanismo di estensione #pragma mentre in Fortran vengono implementate come commenti formattati in modo speciale. Queste direttive istruiscono il compilatore ad eseguire certe trasformazioni di codice per convertire il codice seriale in parallelo.

Sebbene sia possibile implementare OpenMP come trasformazione in puro codice pthreads, questo viene raramente eseguito. Gran parte della meccanica di OpenMP è solitamente integrata in una libreria di runtime separata, che fa parte della suite del compilatore. Per GCC questo è libgomp. Fornisce un set di funzioni di alto livello che vengono utilizzate per implementare facilmente i costrutti OpenMP. È anche interno al compilatore e non è destinato ad essere utilizzato dal codice utente, cioè non è fornito alcun file di intestazione.

Con GCC è possibile ottenere una rappresentazione pseudocodice dell'aspetto del codice dopo la trasformazione di OpenMP. Devi fornire l'opzione -fdump-tree-all, il che comporterà il compilatore che emette un numero elevato di file intermedi per ciascuna unità di compilazione. Il più interessante è il filename.017t.ompexp (che deriva da GCC 4.7.1, il numero potrebbe essere diverso su altre versioni di GCC, ma l'estensione sarebbe comunque .ompexp). Questo file contiene una rappresentazione intermedia del codice dopo che i costrutti OpenMP sono stati abbassati e quindi espansi nella loro corretta implementazione.

consideri il seguente esempio di codice C, salvato fun.c:

void fun(double *data, int n) 
{ 
    #pragma omp parallel for 
    for (int i = 0; i < n; i++) 
    data[i] += data[i]*data[i]; 
} 

Il contenuto di fun.c.017t.ompexp è:

fun (double * data, int n) 
{ 
    ... 
    struct .omp_data_s.0 .omp_data_o.1; 
    ... 

<bb 2>: 
    .omp_data_o.1.data = data; 
    .omp_data_o.1.n = n; 
    __builtin_GOMP_parallel_start (fun._omp_fn.0, &.omp_data_o.1, 0); 
    fun._omp_fn.0 (&.omp_data_o.1); 
    __builtin_GOMP_parallel_end(); 
    data = .omp_data_o.1.data; 
    n = .omp_data_o.1.n; 
    return; 
} 

fun._omp_fn.0 (struct .omp_data_s.0 * .omp_data_i) 
{ 
    int n [value-expr: .omp_data_i->n]; 
    double * data [value-expr: .omp_data_i->data]; 
    ... 

<bb 3>: 
    i = 0; 
    D.1637 = .omp_data_i->n; 
    D.1638 = __builtin_omp_get_num_threads(); 
    D.1639 = __builtin_omp_get_thread_num(); 
    ... 

<bb 4>: 
    ... this is the body of the loop ... 
    i = i + 1; 
    if (i < D.1644) 
    goto <bb 4>; 
    else 
    goto <bb 5>; 

<bb 5>: 

<bb 6>: 
    return; 

    ... 
} 

ho omesso grandi porzioni di uscita per brevità. Questo non è esattamente il codice C. È una rappresentazione simile al C del flusso del programma. <bb N> sono i cosiddetti blocchi di base - raccolta di istruzioni, trattate come blocchi singoli nel flusso di lavoro del programma. La prima cosa che si vede è che la regione parallela viene estratta in una funzione separata. Questo non è raro - la maggior parte delle implementazioni OpenMP fa più o meno la stessa trasformazione del codice. Si può anche osservare che il compilatore inserisce chiamate alle funzioni libgomp come GOMP_parallel_start e GOMP_parallel_end, che vengono utilizzate per eseguire il bootstrap e quindi per terminare l'esecuzione di un'area parallela (il prefisso __builtin_ viene rimosso in seguito). All'interno di fun._omp_fn.0 esiste un ciclo for, implementato in <bb 4> (notare che anche il ciclo stesso è espanso). Inoltre tutte le variabili condivise sono inserite in una struttura speciale che viene passata all'implementazione della regione parallela. <bb 3> contiene il codice che calcola l'intervallo di iterazioni su cui il thread corrente opererebbe.

Beh, non proprio un codice C, ma questa è probabilmente la cosa più vicina che si possa ottenere da GCC.