私が理解している限りでは、#pragma omp parallel
そのバリエーションは基本的に、CPU の数に対応する複数の同時スレッドで次のブロックを実行します。並列化がネストされている場合 (並列 for 内の並列 for、並列関数内の並列関数など)、内部の並列化では何が起こるのでしょうか?
私は OpenMP を初めて使用しており、念頭に置いているケースはおそらくかなり単純なもの、つまりベクトルと行列の乗算です。これは 2 つのネストされた for ループで実行されます。CPU の数がベクトルの要素数よりも少ないと仮定した場合、内部ループを並列で実行することに利点はありますか? スレッドの合計数は CPU の数よりも大きくなりますか、それとも内部ループは順番に実行されますか?
ベストアンサー1
(1)OpenMPにおけるネストされた並列処理:http://docs.oracle.com/cd/E19205-01/819-5270/aewbc/index.html
OMP_NESTED
多くの実装ではこの機能がデフォルトでオフになっており、一部の実装ではネストされた並列処理が完全にサポートされていないため、またはを設定してネストされた並列処理をオンにする必要がありますomp_set_nested
。オンにすると、 を満たすたびにparallel for
、OpenMP は で定義されている数のスレッドを作成しますOMP_NUM_THREADS
。したがって、2 レベルの並列処理の場合、スレッドの合計数は N^2 になります (N = ) OMP_NUM_THREADS
。
このようなネストされた並列処理はオーバーサブスクリプション (つまり、ビジースレッドの数がコア数より多い) を引き起こし、速度向上を低下させる可能性があります。極端なケースでは、ネストされた並列処理が再帰的に呼び出され、スレッドが肥大化 (たとえば、1000 スレッドが作成される) し、コンピューターはコンテキスト切り替えに時間を浪費することになります。このような場合は、 を設定することでスレッドの数を動的に制御できますomp_set_dynamic
。
(2)行列ベクトル乗算の例:コードは次のようになる。
// Input: A(N by M), B(M by 1)
// Output: C(N by 1)
for (int i = 0; i < N; ++i)
for (int j = 0; j < M; ++j)
C[i] += A[i][j] * B[j];
一般的に、外側のループが可能な状態で内側のループを並列化することは、スレッドのフォーク/結合のオーバーヘッドのため好ましくありません。(多くの OpenMP 実装ではスレッドを事前に作成しますが、それでもスレッドにタスクをディスパッチし、並列処理の最後に暗黙のバリアを呼び出す必要があります)
あなたの懸念は、N < CPU の数の場合です。はい、その通りです。この場合、スピードアップは N によって制限され、ネストされた並列処理を可能にすると確実にメリットがあります。
ただし、N が十分に大きい場合、コードによってオーバーサブスクリプションが発生します。次のような解決策を考えています。
- ループ構造を変更して、1 レベルのループのみが存在するようにします。(実行できそうです)
- コードの特殊化: N が小さい場合はネストされた並列処理を実行し、そうでない場合は実行しません。
- を使用したネストされた並列処理。ただし、がスレッドの数とスレッドのアクティビティを
omp_set_dynamic
どのように制御するかを確認してください。実装は異なる場合があります。omp_set_dynamic