2012-01-30 14 views
5

私はコードがまだ十分速いのに2つのノードセット間の距離を計算するCコードを持っていますが、パラレルコンピューティング。私はすでにopenMPに関するいくつかの情報を見つけましたが、私は今それを使用しようとしていますが、ちょっと変わったことがあります。 ompがなければ、コードcpuの時間は20秒で、160秒かかる2つのプラグマ行が追加されます!それはどうしたらできますか?距離計算用のCコード

私は

float computedist(float **vG1, float **vG2, int ncft, int ntri2, int jump, float *dist){ 
    int k = 0, i, j; 
    float min = 0; 
    float max = 0; 
    float avg = 0; 
    float *d = malloc(3*sizeof(float)); 
    float diff; 

    #pragma omp parallel 
    for(i=0;i<ncft;i+=jump){ 
     #pragma omp parallel 
     for(j=0;j<ntri2;j++){ 
      d[0] = vG1[i][0] - vG2[j][0]; 
      d[1] = vG1[i][1] - vG2[j][1]; 
      d[2] = vG1[i][2] - vG2[j][2]; 
      diff = sqrt(pow(d[0],2) + pow(d[1],2) + pow(d[2],2)); 
      if(j==0) 
       dist[k] = diff; 
      else 
       if(diff<dist[k]) 
        dist[k] = diff; 

     } 
     avg += dist[k]; 
     if(dist[k]>max) 
      max = dist[k]; 
     k++; 
    } 

    printf("max distance: %f\n",max); 
    printf("average distance: %f\n",avg/(int)(ncft/jump)); 

    free(d); 

    return max; 
} 

ダウンここに私のコードを追加するには、あなたが外側のループと内部ループの両方に#pragma omp parallelを追加するときは、同期の多くを使用して任意のヘルプ

+0

「どうしたらできますか?」 - 通常の原因は、参照の局所性またはあまりにも多くの同期化(またはその両方)によって、不適切な並列化スキームです。 –

+1

環境変数OMP_NUM_THREADSを1に設定し、OpenMPプログラムを1つのスレッドで実行すると、どれくらい時間がかかりますか? –

+0

@AlexeyKukanov並列ループの前にvoid omp_set_num_threads(int num_threads)を入れても大丈夫ですか? – Nicholas

答えて

5

(以下答えはそれ以来、これらの提案を適用して改善された質問、初期コードを参照)


あなたは、OpenMPを使用する方法の詳細を読む必要があります。仕様はhttp://www.openmp.orgにあります。チュートリアルやその他のリソースへのリンクがあります。

私はあなたのコードにいくつかの問題点を指摘し、それらを修正する方法を提案します。

float *d = malloc(3*sizeof(float)); 
    float diff; 

dデータ競合を避けるために(下記参照)、一時的な変数として使用されているので、#pragma omp parallel forprivateとしてマークする必要があります。一方、動的割り当ての代わりに、私は3つの別々の浮動小数点数を使用します。 diffも一時的な値を保持するので、privateである必要があります。

(領域は任意のワークシェアリング構造物が含まれていないため)あなたは、各スレッドが全体のループを実行する並列領域を作成し
#pragma omp parallel 
    for(i=0;i<ncft;i+=jump){ 
     #pragma omp parallel 
     for(j=0;j<ntri2;j++){ 

、そしてその中に、あなたは、スレッドの新しい(!)セットでネストされた地域を作成し、それぞれ内部ループ全体を実行します。それはあなたのプログラムに多くのオーバーヘッドと不要な計算を追加します。必要なのは#pragma omp parallel forで、外側のループにのみ適用されます。

  d[0] = vG1[i][0] - vG2[j][0]; 
      d[1] = vG1[i][1] - vG2[j][1]; 
      d[2] = vG1[i][2] - vG2[j][2]; 
      diff = sqrt(pow(d[0],2) + pow(d[1],2) + pow(d[2],2)); 

並列処理には関係しませんが、powを四角形の計算に使用するのはなぜですか?良い古い乗算は、読みやすく、高速です。

  if(j==0) 
       dist[k] = diff; 
      else 
       if(diff<dist[k]) 
        dist[k] = diff; 

アクションは(dist[k]=diff;)と同じであるので、コードは||(論理OR)2つの条件を組み合わせることによって簡略化することができます。

 } 
     avg += dist[k]; 
     if(dist[k]>max) 
      max = dist[k]; 

ここでは、外側のループ全体で集計値を計算します。OpenMPでは、これはreduction節の#pragma omp forで行われます。

 k++; 
    } 

現在、あなたは、このように並列コードのデータレースにつながる反復間の不要な依存関係を作成し、各繰り返しでkをインクリメントします。あなたのコードによれば、ki/jumpの便利な "エイリアス"なので、繰り返しの始めにそのように割り当てます。private

+0

私はあなたの提案をすべて適用しましたが、それでも正しく動作しません – Nicholas

2

のためにどうもありがとうございます。

#pragma omp parallelを使用すると、ループの後にbarrierが存在するため、すべてのスレッドは最後のスレッドが終了するまで待機します。
あなたの場合、内部ループと外部ループの両方ですべてのスレッドを待たなければならないので、同期化を使用するためのオーバーヘッドが大きくなります。

障壁の量を最小限に抑えるために、通常、外側のループ[]を[十分な作業があると仮定して...]使用することがベストです。

+0

外側のループに '#pragma omp parallel'だけを置くと、プログラムはバスエラーを返します... – Nicholas

+0

@Nicholas:確かではありませんが、私は' pragma omp parallelをprivateあなたはうまくいくはずです。これは別の問題ですので、問題が解決しない場合はこの問題の詳細を追加して新しい質問を投稿してください。 – amit

+0

私の質問は私的なものを使って更新しました。ちょっと勉強してから:) – Nicholas

0

コードでは、すべてのスレッドに共通の配列distに書き込みます。おそらくあなたはそこで間違った共有の問題を抱えているでしょう。 パディング付きでその配列を割り当てようとします。