5

私はモンテカルロ法を使用して pi を計算し、並列プログラミングと openmp の基本的な経験をしています。

問題は、1 スレッド、x 反復を使用すると、常に n スレッド、x 反復よりも高速に実行されることです。誰でも理由を教えてもらえますか?

たとえば、コードは「a.out 1 1000000」のように実行されます。ここで、1 はスレッド、1000000 は反復です。

include <omp.h>
include <stdio.h>
include <stdlib.h>
include <iostream>
include <iomanip>
include <math.h>

using namespace std;

int main (int argc, char *argv[]) {

double arrow_area_circle, pi;
float xp, yp;
int i, n;
double pitg= atan(1.0)*4.0; //for pi error
cout << "Number processors: " << omp_get_num_procs() << endl;

//Number of divisions
iterarions=atoi(argv[2]); 
arrow_area_circle = 0.0;

#pragma omp parallel num_threads(atoi(argv[1]))
{
srandom(omp_get_thread_num());

#pragma omp for private(xp, yp) reduction(+:arrow_area_circle) //*,/,-,+
for (i = 0; i < iterarions; i++) {
    xp=rand()/(float)RAND_MAX;
    yp=rand()/(float)RAND_MAX;

    if(pow(xp,2.0)+pow(yp,2.0)<=1) arrow_area_circle++;
}
}

pi = 4*arrow_area_circle / iterarions;
cout << setprecision(18) << "PI = " << pi << endl << endl;
cout << setprecision(18) << "Erro = " << pitg-pi << endl << endl;

return 0;
}
4

5 に答える 5

10

このような CPU を集中的に使用するタスクは、システム内の CPU よりも多くのスレッドで作業を行うと遅くなります。単一の CPU システムで実行している場合、複数のスレッドを使用すると確実に速度が低下します。これは、OS がさまざまなスレッドを切り替える必要があるためです。これは純粋なオーバーヘッドです。このようなタスクでは、コアと同じ数のスレッドが理想的です。

もう 1 つの問題は、arrow_area_circle がスレッド間で共有されることです。各コアでスレッドを実行している場合、arrow_area_circle をインクリメントすると、他のコアのキャッシュ内のコピーが無効になり、再フェッチが必要になります。数サイクルかかるはずの arrow_area_circle++ は、数十または数百サイクルかかります。スレッドごとに arrow_area_circle を作成し、最後にそれらを結合してみてください。

編集: Joe Duffy は、スレッド間でデータを共有するコストに関するブログ エントリを投稿しました。

于 2009-10-20T01:20:20.003 に答える
7

ある種の自動並列化コンパイラを使用しているようです。システムに 1 つ以上のコア/CPU があると仮定します (あまりにも明白なので、Intel のマーケティングが何を信じさせようと、Pentium 4 のハイパースレッディングは 2 つのコアを持っているとは見なされません)。 .) 2 つの問題があります。最初のものは些細なことで、おそらくあなたの問題ではありません:

  1. 変数 arrow_area_circle がプロセス間で共有されている場合、arrow_area_circle++ を実行すると、インターロック命令が使用され、アトミックに健全な方法で値が同期されます。内部ループで arrow_area_circle をインクリメントする代わりに、「プライベート」変数をインクリメントしてから、その値を最後に一度だけ共通の arrow_area_circle 変数に追加する必要があります。

  2. rand() 関数が正常に動作するには、クリティカル セクションを使用して内部的に実行する必要があります。その理由は、その内部状態/シードが静的共有変数であるためです。そうでない場合、2 つの異なるプロセスが rand() をほぼ同時に呼び出しているという理由だけで、非常に高い確率で rand() から同じ出力を取得する可能性があります。これは、rand() の実行が遅くなることを意味します。特に、より多くのスレッド/プロセスが同時にそれを呼び出している場合はそうです。arrow_area_circle 変数 (アトミック インクリメントのみが必要) とは異なり、状態の更新がより複雑であるため、真のクリティカル セクションは rand() によって呼び出される必要があります。これを回避するには、独自の乱数ジェネレーターのソース コードを入手し、それを非公開で使用する必要があります。シードまたは状態。ほとんどのコンパイラの標準の rand() 実装のソース コードは広く入手できます。

また、pow(,) 関数を使用して x * x と同じことを実行していることも指摘したいと思います。後者は前者の約 300 倍高速です。この点は、あなたが求めている質問とは関係ありませんが。:)

于 2009-10-20T01:40:37.387 に答える
2

rand() はブロッキング関数です。内部にクリティカルセクションがあることを意味します。

于 2009-11-16T21:01:07.790 に答える
2

コンテキストの切り替え。

于 2009-10-20T01:19:32.777 に答える
1

並列設定で乱数を使用する場合は、細心の注意を払う必要があることを強調してください。実際、SPRNGのようなものを使用する必要があります

何をするにしても、各スレッドが同じ乱数を使用していないことを確認してください。

于 2009-11-05T14:20:24.227 に答える