5

ここには、比較的単純な OpenMP コンストラクトであると私が理解しているものがあります。問題は、2 つのスレッドと比較して、1 つのスレッドでプログラムが約 100 ~ 300 倍高速に実行されることです。プログラムの 87% が で費やされgomp_send_wait()、さらに 9.5% が で費やされgomp_send_postます。

プログラムは正しい結果を返しますが、リソースの競合を引き起こしているコードに欠陥があるのか​​ 、それともチャンクサイズ4のループではスレッド作成のオーバーヘッドが大幅に価値がないだけなのか疑問に思います p。シミュレートしている分子のサイズに応じて、17 から 1000 までです。

私の数値は、p が 17 でチャンク サイズが 4 の場合の最悪の場合のものです。パフォーマンスは、静的、動的、またはガイド付きスケジューリングのいずれを使用しても同じです。p=150と チャンク サイズを使用75しても、プログラムはシリアルよりも 75 倍から 100 倍遅くなります。

...
    double e_t_sum=0.0;
    double e_in_sum=0.0;

    int nthreads,tid;

    #pragma omp parallel for schedule(static, 4) reduction(+ : e_t_sum, e_in_sum) shared(ee_t) private(tid, i, d_x, d_y, d_z, rr,) firstprivate( V_in, t_x, t_y, t_z) lastprivate(nthreads)
    for (i = 0; i < p; i++){
        if (i != c){
            nthreads = omp_get_num_threads();               
            tid = omp_get_thread_num();

            d_x = V_in[i].x - t_x; 
            d_y = V_in[i].y - t_y;
            d_z = V_in[i].z - t_z;


            rr = d_x * d_x + d_y * d_y + d_z * d_z;

            if (i < c){

                ee_t[i][c] = energy(rr, V_in[i].q, V_in[c].q, V_in[i].s, V_in[c].s);
                e_t_sum += ee_t[i][c]; 
                e_in_sum += ee_in[i][c];    
            }
            else{

                ee_t[c][i] = energy(rr, V_in[i].q, V_in[c].q, V_in[i].s, V_in[c].s);
                e_t_sum += ee_t[c][i]; 
                e_in_sum += ee_in[c][i];    
            }

            // if(pid==0){printf("e_t_sum[%d]: %f\n", tid, e_t_sum[tid]);}

        }
    }//end parallel for 


        e_t += e_t_sum;
        e_t -= e_in_sum;            

...
4

6 に答える 6

6

まず、この場合、シリアル コードを最適化しても、OpenMP のジレンマの解決に役立つとは思えません。ご心配なく。

IMO では、速度低下について 3 つの考えられる説明があります。

  1. これは減速を簡単に説明できます。配列 ee_t の要素は、キャッシュ ライン内で誤った共有を引き起こしています。偽共有とは、コアが実際にデータを共有しているからではなく、コアが書き込んでいるものがたまたま同じキャッシュ ラインにある場合に、コアが同じキャッシュ ラインに書き込んでしまう場合です (これが共有と呼ばれる理由です)。Googleで偽の共有が見つからない場合は、さらに説明できます. ee_t 要素のキャッシュ ラインを整列させると、非常に役立つ場合があります。

  2. スポーン作業のオーバーヘッドは、並列処理の利点よりも高くなります。8 コア未満で試しましたか? 2コアでの性能は?

  3. 反復の総数は少なく、例として 17 とします。それを 8 つのコアに分割すると、負荷の不均衡の問題が発生します (特に、反復の一部が実質的に何の作業も行っていないため (i == c の場合)。少なくとも 1 つのコアは 3 回の反復を実行する必要がありますが、他のすべてのコアはdo 2. これはスローダウンを説明するものではありませんが、スピードアップが期待したほど高くない理由の 1 つであることは確かです. 反復の長さはさまざまであるため、チャンク サイズが 1 の動的スケジュールを使用するか、openmp ガイドを使用します.チャンク サイズを試してみてください。小さすぎるチャンクも速度低下につながります。

それがどうなるか教えてください。

于 2011-05-10T00:47:38.077 に答える
2

シリアル コードをマルチスレッド モードで実行すると、パフォーマンスが向上するのは当然だと考えているようです。それは与えられたものではありません。そして、それはしばしば真実ではありません。ループを並列化して複数のスレッドまたは複数のプロセッサで実行すると、必ずしもパフォーマンスが向上するとは限りません。ほとんどの場合、何らかの再構築が必要です。あなたの場合、コードは良いシリアルコードではありません。

シリアルコードの最適化に関する本には、ループのルール番号 1 として、すべての条件付き演算を削除するというものがあります。テスト費用。一部のコンパイラ (ちなみに、使用している OS/コンパイラ/プロセッサを言うことは決してありません.. それは重要です) は、条件付きコードを最適化しようとすることができます。一部のコンパイラ (Sun の C コンパイラなど) では、「収集」モードでプログラムを実行することもできます。このモードでは、条件の分岐が行われる頻度に関するランタイム プロファイル情報が生成され、収集されたデータを使用するモードで再コンパイルできます。生成されたコードを最適化します。(-xprofile オプションを参照)

並列コードを最適化するための最初のルールは、最初にできる限り最善のシリアル最適化を行うことです。次に、ループを並列化します。

条件をループの外に移動し、Metiu が示唆するように、コードを 2 つの別個のループとして書き直すことにより、オプティマイザーがより適切に作業できるソースを提供します。シリアルコードの方がうまく動作し、並列化されたコードは驚くほど並列です。

それでも、結果は OS/コンパイラ/プラットフォームによって異なる場合があります。

OpenMPSolaris アプリケーション プログラミングの使用を参照してください。

于 2009-05-17T06:19:27.343 に答える
1

まず、チャンクサイズをさらに大きくしてみてください。スレッドの作成にはオーバーヘッドが伴いますが、スレッドごとに新しい作業を取得することも同様であり、粒度はそれを圧倒するのに十分な大きさである必要があります。

1つの大きな可能性:GOMPの削減の実装は非常に貧弱であり(プロファイルデータによって示唆されます)、各スレッド内に蓄積して最後に収集するのではなく、各ブロックの後にメッセージを生成します。それぞれ要素を含む配列として割り当て、ループ内に追加してから、それらをループして、並列ループの終了後にグローバル合計を計算してみe_t_sumてください。e_in_sumnthreadse_t_sum[tid]

これにより、これらの配列の複数の要素が共通のキャッシュライン内にあり、複数のプロセッサがこの同じキャッシュラインに書き込みを行うという、潜在的な偽共有の問題が発生することに注意してください。キャッシュを共有するコアのセットでこれを実行する場合、これは問題ありませんが、他の場所で悪臭を放ちます。

別の可能性:ee_tの要素の更新で誤った共有が発生している可能性があります。この配列の配置を確認し、キャッシュラインサイズの倍数であるチャンクサイズを試してください。この病状の微妙なヒントの1つは、ループi > cの一部であり、。の部分よりも不釣り合いに長くかかることi < cです。

于 2009-06-18T02:17:13.990 に答える
1

ループ内のすべてのブランチ (つまり if) を移動して、i < c 用と i > c 用の 2 つの別々のループで実行する必要があると思います。これはシングルスレッドのコードにも大きなメリットをもたらしますが、スレッド作成のオーバーヘッドが小さな n のメリットよりも大きくなる可能性があるとしても、より多くの並列処理が得られるはずです。

于 2009-05-15T21:42:55.267 に答える
0

これは、GNU コンパイラの openmp 実装の問題のようです。別のコンパイラを試してください。Intel には Linux コンパイラがあり、ここからコピーをダウンロードして試すことができます。

私が気付いたもう1つのことは、あなたが持っているfirstprivate変数がまったく不要であるように見えることです. 配列 V_in のプライベート コピーを作成すると、かなりのオーバーヘッドが発生する可能性があり、これが問題になる可能性があります。

あなたの問題はこれら2つの問題の1つであると思います。

于 2009-05-22T13:06:15.440 に答える