2011-10-07 27 views
5

ここで私が取り組んでいるOpenCLカーネルの2つのコードがあります。彼らは大幅に異なるランタイムを表示します。OpenCL:なぜこれらの2つのケースでパフォーマンスが大きく異なるのですか?

コードはかなり複雑なので、私はすぐに簡略化しました。

このバージョンでは、1秒の下で実行されます:

for (int ii=0; ii<someNumber;ii++) 
{ 
    for (int jj=0; ii<someNumber2;jj++) 
    { 
     value1 = value2 + value3; 
     value1 = value1 * someFunction(a,b,c); 
     double nothing = value1; 
    } 
} 

と、このバージョンを実行するために周りに38秒かかる:私が言うように

for (int ii=0; ii<someNumber;ii++) 
{ 
    for (int jj=0; ii<someNumber2;jj++) 
    { 
     value1 = value2 + value3; 
     value1 = value1 * someFunction(a,b,c); 
    } 
    double nothing = value1; 
} 

、コードがあります(これよりも多少複雑ですループ内では他にも多くのことが起こっています)、変数 "nothing"はブレースの直前から直後に移動します。

私はOpenCLにとって非常に新しく、何が起こっているのか、それを修正する方法ははるかに少ないです。言うまでもなく、遅いケースは実際に私の実装で必要なものです。私はアドレス空間(ここではすべての変数は__privateにあります)を使いこなそうとしました。

私は何らかの理由で、ブレースが閉じたときに、GPUが変数 "value1"をより遅いメモリにプッシュしていることが想像できます。これは可能性のある説明ですか?私に何ができる?

ありがとうございます!

更新:これも1秒未満で実行されます(ただし、どちらの行のコメントも付いていないので、極端に遅くなります)。これはループに他の変更を加えることなく、value1は以前と同じ場所で宣言されています。

for (int ii=0; ii<someNumber;ii++) 
{ 
    for (int jj=0; ii<someNumber2;jj++) 
    { 
//  value1 = value2 + value3; 
//  value1 = value1 * someFunction(a,b,c); 
    } 
    double nothing = value1; 
} 

UPDATE 2:示されるようにコードが実際value1の宣言と、このような別のループ内にネストされた:

double value1=0; 
for (int kk=0; kk<someNumber3;kk++) 
{ 
    for (int ii=0; ii<someNumber;ii++) 
    { 
     for (int jj=0; ii<someNumber2;jj++) 
     { 
      value1 = value2 + value3; 
      value1 = value1 * someFunction(a,b,c); 
     } 
     double nothing = value1; 
    } 
} 

value1が宣言される移動はまた、迅速な場合に私たちを取り戻します。

for (int kk=0; kk<someNumber3;kk++) 
{ 
    double value1=0; 
    for (int ii=0; ii<someNumber;ii++) 
    { 
     for (int jj=0; ii<someNumber2;jj++) 
     { 
      value1 = value2 + value3; 
      value1 = value1 * someFunction(a,b,c); 
     } 
     double nothing = value1; 
    } 
} 

OpenCLは非常にトリッキーなアートです!私はまだ何が起こっているのか本当に理解していないが、少なくとも私はそれを今修正する方法を知っている!

+0

これはかなり奇妙です。より低速なバージョンを使用する必要がありますか?これらのスニペットから、機能的には同じように見えます。 – Chriszuma

+0

ご返信ありがとうございます。はい、私は確信していますが、私が与えた例は機能的に同一であるということは間違いありません。内側の中括弧内のコードは+ =を持つべきです。 – carthurs

+0

これらのコードスニペットに基づいて、2番目のものが遅くなる必要はありません。私は、割り当てを移動すると、分岐の増加(1つのワークユニットが 'if'を実行し、次にelseが' else'を実行する)などの副作用があるはずだと推測します。 –

答えて

4

どのような実装を使用していますか?私は "double nothing = value1;"と期待します。任意の合理的なコンパイラによって、いずれの場合にもデッドコードとして排除される。

+0

私はあなたの記事のおかげで問題を見つけたと思う。ケース1(私の質問からの最初のボックス)では、私はコンパイラが「デッドコードとして削除する」と内部ループを最適化すると思います。ケース2では、変数 'value1'が内側ループの外側に必要であることを認識して実行します。関数 'someFunction(a、b、c)'は非常に遅いので、これは減速を引き起こします。 FYIの実装はLinux用のAMDのSDKです。 皆さん、ありがとうございます! – carthurs

+0

value1は使用されていないので、コンパイラはsomeFunctionへの呼び出しを最適化します。 someFunctionに副作用がないことをどのように確認できますか? – vocaro

+0

「何も使用しない」ためです。私はvalue1について話していませんでした。 – arsenm

2

最初のケースは1つのループ(コンパイラの最適化あり)です しかし、2番目のループはネストされたループを持つループです。それが大きな問題です。

ループを開始する前にプライベート変数(somenumberとsomenumber2)として保存することをお勧めします。これは、グローバル変数とローカル変数のチェックがたくさんあります。このようにして、毎回プライベートデータをチェックします。 個人的な経験として、OpenCLループのチェックケースとして使用されるすべてのvarはプライベートでなければなりません。 これにより、グローバルメモリアクセスの最大80%を節約できます。 (ループが非常に短いか、シンプルである場合は特に)

例として、これは高速で動作するはずです:

int c_somenumber = someNumber; 
for (int ii=0; ii<c_someNumber;ii++) 
{ 
    int c_somenumber2 = someNumber2;  
    for (int jj=0; ii<c_someNumber2;jj++) 
    { 
     value1 = value2 + value3; 
     value1 = value1 * someFunction(a,b,c); 
    } 
    double nothing = value1; 
} 

EDIT: また、VALUE1は、プライベートメモリにキャッシュされるべきである(SHOULD)。(あなたが最後の編集で行ったように)

+0

うん、すべてがプライベートとして保存されています。他の答えに対するコメントとして私の説明を参照してください! – carthurs