0

配列を作成し、OpenMP を使用してその配列の各位置に値を割り当てるプログラムを作成しようとしています。配列がどの位置を担当するかを指定したいことを除けば、それは些細なことです。

たとえば、長さが 80 で 8 スレッドの配列がある場合、スレッド 0 が位置 0 ~ 9、スレッド 1 ~ 10 ~ 19 などにのみ書き込むようにします。

私はOpenMPに非常に慣れていないので、次のことを試しました:

#include <omp.h>
#include <stdio.h>
#define N       80

int main (int argc, char *argv[]) 
{
    int nthreads = 8, tid, i, base, a[N];

    #pragma omp parallel
    {
        tid = omp_get_thread_num();
        base = ((float)tid/(float)nthreads) * N;
        for (i = 0; i < N/nthreads; i++) {
            a[base + i] = 0;
            printf("%d %d\n", tid, base+i);
        }
    }
    return 0;
}

ただし、このプログラムは、予想どおり、すべての位置にアクセスするわけではありません。出力は実行するたびに異なります。たとえば、次のようになります。

4 40
5 51
5 52
5 53
5 54
5 55
5 56
5 57
5 58
5 59
5 50
4 40
6 60
6 60
3 30
0 0
1 10

ディレクティブが欠落していると思いますが、それがどれなのかわかりません。

4

3 に答える 3

1

あなたはかなり欠けています。ディレクティブ

#pragma omp parallel

次のコード ブロックが、基本的にすべてのスレッドによって並列に実行されることをランタイムに通知するだけです。ただし、作業がスレッド間で共有されることは指定されておらず、すべてのスレッドがブロックを実行することだけが指定されています。作業を共有するには、コードに次のような別のディレクティブが必要です

#pragma omp parallel
{
    #pragma omp for 
    ...

これは、forスレッド間で作業を分散するディレクティブです。

しかし、OpenMP の構文に慣れていないことよりも深刻な、プログラムの設計ミスを犯しています。あなたが提案するように、スレッド間で作業を手動で分解することは、OpenMP がプログラマーが回避できるように設計されていることです。自分で分解を行おうとすると、OpenMP のグレインに対してプログラミングを行うことになり、次の 2 つのリスクが生じます。

  1. 物事を間違えること。特に、コンパイラーとランタイムがあなたの側の努力や考えなしで正しくなるという間違った問題を取得することです。
  2. シリアル プログラムよりも実行速度が遅い並列プログラムを慎重に作成すること。

スレッドへの作業の割り当てをある程度制御したい場合は、schedule句を調べてください。次のように並列領域を開始することをお勧めします (2 つのディレクティブを 1 つのステートメントに融合していることに注意してください)。

#pragma omp parallel for default(none) shared(a,base,N)
{
    for (i = 0; i < N; i++) {
        a[base + i] = 0;
}

変数のアクセシビリティを指定したことにも注意してください。これは、特に OpenMP を学習する場合に有効な方法です。コンパイラはi自動的に非公開にします。

私が書いたように、ランタイムは反復をiスレッドごとにチャンクに分割します。最初のスレッドが取得さi = 0..N/num_threadsれ、2 番目のスレッドが取得i = (N/num_threads)+1..2N/num_threadsされます。

schedule後で、句を明示的にディレクティブに追加できます。私が上に書いたことは

#pragma omp parallel for default(none) shared(a,N) schedule(static)

実験することもできます

#pragma omp parallel for default(none) shared(a,N) schedule(dynamic,chunk_size)

通常の場所で十分に文書化されている他の多くのオプション。

于 2013-11-13T13:05:48.103 に答える
1

意図したとおりに動作するようにする方法は、外側の (並列) ループとして 8 回の反復だけのループを作成し、各スレッドに適切な要素だけにアクセスする内側のループを実行させることです。

#pragma omp parallel for private(j)
   for(i = 0; i < 8; i++) {
     for(j = 0; j < 10; j++) {
       a[10*i+j] = 0;
       printf("thread %d updated element %d\n", omp_get_thread_num(), 8*i+j);
     }
   }

私は今これをテストすることができませんでしたが、90% はあなたが望んでいることを正確に行うと確信しています (そして、このようにすると、物事がどのように機能するかを「完全に制御」できます)。ただし、これは最も効率的な方法ではない可能性があります。1 つには、一連の要素をゼロに設定したいだけの場合memsetは、ループではなく のような組み込み関数を使用する必要があります...

于 2013-11-13T13:48:04.483 に答える