2013-02-13 14 views
6

私は自分のコードをGCCによって自動ベクトル化できるようにするために取り組んできましたが、-fopenmpフラグを含めると自動ベクトル化をすべて停止しようとしています。私はftree-vectorize -ftree-vectorizer-verbose=5をベクトル化して監視しています。OpenMPを使用するとGCCの自動ベクトル化が停止する

もし私がフラグを含んでいなければ、それはベクター化されていて、なぜそうでなければ、それぞれのループに関する多くの情報を提供し始めます。コンパイルは、リンクできないので、omp_get_wtime()関数を使用しようとすると停止します。フラグがインクルードされると、すべての関数がリストされ、0個のループがベクトル化されていることがわかります。

私は問題が言及されたいくつかの場所を読んだが、実際には解決策には至っていません。http://software.intel.com/en-us/forums/topic/295858http://gcc.gnu.org/bugzilla/show_bug.cgi?id=46032。 OpenMPはベクトル化を扱う独自の方法を持っていますか?明示的にそれを伝える必要がありますか?

+0

私はあなたがこの質問に[回答](http://stackoverflow.com/a/14717689/771663)で賢明な情報を見つけることができると思います。 – Massimiliano

+0

OpenMPでSIMDを使用する方法を説明していただきありがとうございますが、OpenMPを使用するとSIMDの実装がうまく動作しない理由を説明していないようです。両方を使用する方法はありませんか? – superbriggs

+1

これはまた、私は同じ数のビットでしか動作できないことを意味し、それらは数の間でちょうど分かれています。 GCCでそれをやっている間、私はレジスタに分割したいと思った人の数を尋ねられませんでした。私は大学の「スーパーコンピュータ」を使っているので、ハードウェアにはSIMD用のスペースが余分にあると仮定していました。それが正しいかどうかわかるでしょうか? – superbriggs

答えて

9

最近のGCCバージョンで解決されたように見えるGCCベクトル化装置には欠点があります。首尾よく次のような単純なループ私のテストケースGCC 4.7.2 vectorisesで:

同じ時間のGCC 4.6.1でないと、それはループを分析することができない関数呼び出しやデータの参照が含まれていることを、文句を言います。ベクトル化のバグは、parallel forのループがGCCによって実装される方法によって引き起こされます。OpenMPの構築物が処理され、展開されている場合は、単純なループコードは、これに似た何かに変換される:4.7前

struct omp_fn_0_s 
{ 
    int N; 
    double *a; 
    double *b; 
    double *c; 
    double d; 
}; 

void omp_fn_0(struct omp_fn_0_s *data) 
{ 
    int start, end; 
    int nthreads = omp_get_num_threads(); 
    int threadid = omp_get_thread_num(); 

    // This is just to illustrate the case - GCC uses a bit different formulas 
    start = (data->N * threadid)/nthreads; 
    end = (data->N * (threadid+1))/nthreads; 

    for (int i = start; i < end; i++) 
     data->a[i] = data->b[i] + data->c[i] * data->d; 
} 

... 

struct omp_fn_0_s omp_data_o; 

omp_data_o.N = N; 
omp_data_o.a = a; 
omp_data_o.b = b; 
omp_data_o.c = c; 
omp_data_o.d = d; 

GOMP_parallel_start(omp_fn_0, &omp_data_o, 0); 
omp_fn_0(&omp_data_o); 
GOMP_parallel_end(); 

N = omp_data_o.N; 
a = omp_data_o.a; 
b = omp_data_o.b; 
c = omp_data_o.c; 
d = omp_data_o.d; 

GCCでvectoriserは、そのループをvectoriseに失敗しました。これはOpenMP固有の問題ではありません。 OpenMPコードなしで簡単に再現できます。 - なぜならエイリアシングが起こらないことを指定するために使用restrictキーワードでも等しくvectoriseすべき

struct fun_s 
{ 
    double *restrict a; 
    double *restrict b; 
    double *restrict c; 
    double d; 
    int n; 
}; 

void fun1(double *restrict a, 
      double *restrict b, 
      double *restrict c, 
      double d, 
      int n) 
{ 
    int i; 
    for (i = 0; i < n; i++) 
     a[i] = b[i] + c[i] * d; 
} 

void fun2(struct fun_s *par) 
{ 
    int i; 
    for (i = 0; i < par->n; i++) 
     par->a[i] = par->b[i] + par->c[i] * par->d; 
} 

一つは、両方のコード(!ここにはOpenMPの通知が)ことを期待する。このことを確認するために、私は、次の簡単なテストを書きました。残念ながら、これはGCC < 4.7では当てはまりません。fun1でループをベクトル化しますが、fun2ではOpenMPコードをコンパイルするときと同じ理由を挙げてベクトル化できません。

この理由はvectoriserがpar->apar->b、およびpar->cポイントはにことをpar->dがメモリ内にないことを証明することができないということです。これは、常に2つのケースが考えられるfun1、の場合ではありません。

  • dは、レジスタの値を引数として渡されます。
  • dは、スタック上で値の引数として渡されます。

x64システムでは、最初のいくつかの浮動小数点引数がXMMレジスタ(AVX対応CPUのYMM)に渡されることが必須です。つまり、dがこの場合に渡され、ポインタがポインタを指すことはできません。ループがベクトル化されます。 x86システムでは、ABIは引数がスタックに渡されるように指示します。したがって、dは3つのポインタのいずれかによってエイリアスされることがあります。実際、GCCは、-m32オプションを持つ32ビットx86コードを生成するよう指示された場合には、fun1のループをベクトル化することを拒否します。

GCC 4.7では、実行時チェックを挿入することにより、dpar->dのどちらにもエイリアスが発生しないようにしています。 d退治

はunprovable非エイリアシングと次のOpenMPコードはGCC 4.6.1でベクトル化されます削除:

#pragma omp parallel for schedule(static) 
for (int i = 0; i < N; i++) 
    a[i] = b[i] + c[i]; 
+0

すばらしい答え。しかし、「これはちょうどその事例を説明することです - GCCは少し違う公式を使用しています。 GCCはどのような公式を使用していますか? –

+0

@Zboson、私はここに貼り付けることができますが(醜いですが)、 'gcc -fdump-tree-all -fopenmp foo.c'を実行して、OpenMP拡張の後に' fooにあるASTを調べてください。 c.015t.ompexp'。違いは、GCCが最初の 'r'スレッドに1回の追加の反復を与えることによって、' r = N%num_threads'部門からの残余を分配することです。 –

3

私はあなたの質問に簡単に答えようとします。

  1. OpenMPには独自のベクター化処理方法がありますか?

はい...ただし、着信OpenMP 4.0から始まります。上に掲載されたlinkは、この構成についての良い洞察を提供します。一方、現在のOpenMP 3.1では、SIMDの概念を認識していません。したがって、実際には(少なくとも私の経験では)何が起こるかは、ループ上でopenmpワークシェアリングコンストラクトが使用されるたびに自動ベクトル化メカニズムが禁止されるということです。いずれにしても、2つの概念は直交していて、それでも両方から利益を得ることができます(この他のanswerを参照)。

  1. 明示的に伝える必要はありますか?

私は恐れますが、少なくとも現在は、恐れています。検討中のループを書き直して、ベクトル化を明示的にする方法で開始します(つまり、インテルプラットフォームでイントリンシック、IBMでAltivecなどを使用します)。

+0

ありがとうございました。最初のリンクは 'VECTOR_ADD'関数を与えます。私はそれを行うために1つの通常のサイズのレジスタを使用していることを読んだので、小さな数字だけベクトル化することができます。私は自分のハードウェアがSIMDを処理する特定のレジスタを持っているので、それが起こらないことを知っています。 OpenMPにこのレジスタを使用させる方法はありますか?私はGCCの前にそれをすべて考慮して、これらの機能を使う必要がありますか?なぜOpenMPがこのフォームの動作を止めるのか分かりません。あなたの第二のリンクは、彼らが共に働くことができると言っていますが、私はこれをどのように克服するのでしょうか。もう一度ありがとうございます。 – superbriggs

+0

主な考え方は、VECTOR_ADDで処理するので、OpenMPはSIMDizationを認識してはならないということです。私は3Dnowを一度も使用しませんでしたが、インテルのプラットフォームでは、コードを明示的にベクトル化するために[組み込み関数](http://software.intel.com/en-us/articles/how-to-use-intrinsics)を使用することができます。主な欠点は、移植性(他のプラットフォームでは組み込み関数が動作しないため)や可読性/保守性(条件付きコンパイルのため)を失うことです。 – Massimiliano

+0

このプロジェクトでは、保守性と移植性は重要ではありません。私は現在VECTOR_ADDを使用していません。GCCが何が起こっているのかを確認して自動的にベクター化する方法でループに入れています。 – superbriggs

1

OpenMPが有効になっているときにGCCがベクトル化を実行できない理由は何ですか?

GCCのバグであり得ることを思わ:) http://gcc.gnu.org/bugzilla/show_bug.cgi?id=46032

そうでない場合は、OpenMPのAPIは、自動ベクトル化を防止し、依存関係(制御やデータのいずれか)を導入することができます。自動的に頂点化するためには、与えられたコードがデータ/制御依存性のないものでなければならない。 OpenMPを使用すると、偽の依存関係が発生する可能性があります。

注:OpenMP(4.0以前)は、SIMD /ベクトル化と直交するスレッドレベルの並列処理を使用することです。プログラムはOpenMPとSIMD並列処理を同時に使用できます。

1

GCC 4.9のオプションについてのコメントを探している間、私はこの記事に出会っopenmp- simdは、omp parallel(スレッド化)を有効にせずにOpenMP 4 #pragma omp simdを有効にする必要があります。 gcc bugzilla pr60117(確認済)は、プラグマなしで発生した自動ベクトル化をプラグマompが防止するケースを示しています。

gccはsimd句であってもomp parallelをベクトル化しません(並列領域は、並列にネストされた内側ループのみを自動ベクトル化できます)。私はicc 14.0.2以外のコンパイラについては知らないが、これはsimdのための#pragma omp parallelの実装に推奨できる。他のコンパイラでは、この効果を得るためにSSE組み込みコードが必要になります。

マイクロソフトのコンパイラは、テストで並列領域内で自動ベクトル化を実行しません。そのようなケースでは、gccの優位性が明らかです。

最高の実装であっても、単一ループの並列化とベクトル化にはいくつかの困難があります。並列ループにベクトル化を追加することで、2倍または3倍以上のスピードアップを見ることはめったにありません。例えば、AVX doubleデータ型を使用したベクトル化は、チャンクサイズを効果的に4分の1にカットします。一般的な実装では、配列全体がアライメントされている場合のみアライメントされたデータチャンクを得ることができ、チャンクもベクトル幅の正確な倍数。チャンクがすべて整列していない場合、変化する整列のために固有の作業の不均衡があります。

関連する問題