2013-02-14 4 views
6

OpenMPは実際にはpthreadsにコンパイルされたマクロのセットであることを理解しています。残りのコンパイルが行われる前にpthreadコードを見る方法はありますか? GCCを使ってコンパイルしています。openmpをpthreadsにコンパイルするCコード

答えて

8

最初に、OpenMPはではありません。単純なマクロセットです。 pthreadのようなコードへの単純な変換が見られるかもしれませんが、OpenMPはそれ以上のランタイムサポートを必要とします。

GCCのOpenMPの実装がコンパイラのバックエンド(またはミドルエンド)で行われているため、少なくともGCCでは、pthreadされたコードは表示されません。変換はIR(中間表現)レベルで行われます。したがって、プログラマーの観点からは、コードが実際にどのように変換されているかを見るのは容易ではありません。

しかし、いくつかの参考文献があります。

(1)インテルのエンジニアは、インテルC/C++コンパイラでのOpenMPの実装の偉大な概要を提供:

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)あなたは見てみましょうことGCCのOpenMPの実装の:

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

012を参照してください。はpthreadを使用し、loop.cには並列ループ構成の実装が含まれています。

-1

openmpでテストしていません。しかし、コンパイラオプション-Eは、前処理後にコードを提供するはずです。

+0

これはありません。 'gcc -E'は前処理を行いますが、' #pragma'は解釈されません。 –

5

OpenMPは、マクロではなく、コンパイラディレクティブのセットです。 C/C++では、これらのディレクティブは#pragma拡張メカニズムで実装されていますが、Fortranでは特別なフォーマットのコメントとして実装されています。これらの指示文は、シリアルコードをパラレルに変換するために、特定のコード変換を実行するようにコンパイラに指示します。

純粋なpthreadsコードへの変換としてOpenMPを実装することは可能ですが、これはめったに行われません。 OpenMPの仕組みの大部分は通常、コンパイラスイートの一部として提供される別々のランタイムライブラリに組み込まれています。 GCCの場合、これはlibgompです。これは、OpenMP構成を簡単に実装するために使用される一連の高レベル関数を提供します。コンパイラの内部でもあり、ユーザコードで使用することを意図していません。つまり、ヘッダファイルは提供されていません。

GCCでは、OpenMP変換後のコードの擬似コード表現を得ることができます。 -fdump-tree-allオプションを指定する必要があります。このオプションを指定すると、コンパイル単位ごとに多数の中間ファイルが生成されます。最も興味深いものはfilename.017t.ompexpです(これはGCC 4.7.1に由来しますが、数字は他のGCCバージョンでは異なるかもしれませんが、拡張子はまだ.ompexpです)。このファイルには、OpenMP構造体を下げてから適切な実装に展開した後のコードの中間表現が含まれています。fun.c.017t.ompexpの含有量が

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

fun.cとして保存された次のサンプルCコードは、検討

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; 

    ... 
} 

Iは、簡潔にするため、出力の大部分を省略しています。これはまさにCコードではありません。それはプログラムフローのCのような表現です。 <bb N>は、いわゆるベーシックブロック - プログラムのワークフローで単一ブロックとして扱われるステートメントの集合です。最初に見られるのは、並列領域が別の関数に抽出されるということです。これは珍しいことではありません。大部分のOpenMP実装は、ほぼ同じコード変換を行います。コンパイラはGOMP_parallel_startGOMP_parallel_endのようなlibgomp関数への呼び出しを挿入し、これを使ってブートストラップし、その後パラレル領域(__builtin_という接頭辞は後で削除されます)の実行を終了します。 fun._omp_fn.0の内部にはというループがあり、<bb 4>に実装されています(ループ自体も展開されています)。また、すべての共有変数は、並列領域の実装に渡される特別な構造に入れられます。 <bb 3>には、現在のスレッドが動作する反復の範囲を計算するコードが含まれています。

まあまあCコードではありませんが、これはおそらくGCCから得られる最も近いものです。

関連する問題