2

次のループは、グラフのすべてのエッジを反復処理し、エンドノードが同じグループに属しているかどうかを判断してから、そのグループの合計エッジウェイトにエッジウェイトを追加します。

// TODO: parallel
FORALL_EDGES_BEGIN((*G)) {
    node u = EDGE_SOURCE;
    node v = EDGE_DEST;
    cluster c = zeta[u];
    cluster d = zeta[v];
    if (c == d) {
        // TODO: critical section
        intraEdgeWeight[c] += G->weight(u, v);
    } // else ignore edge
} FORALL_EDGES_END();

OpenMPと並列化したいと思います。ifステートメントのコードは、スレッドが途中で中断された場合に競合状態や誤った結果につながる可能性のあるクリティカルセクションだと思います(正しいですか?)。

+=アトミックに操作できれば、問題は解決したと思います(正解?)。ただし、ディレクティブを調べたところ、次のように記載されています。atomic

残念ながら、アトミック性の設定は、インクリメント、デクリメント、XORなど、通常は単一のプロセッサオペコードにコンパイルできる単純な式にのみ指定できます。たとえば、関数呼び出し、配列のインデックス付け、オーバーロードされた演算子、非PODタイプ、または複数のステートメントを含めることはできません。

このループを正しく並列化するには、何を使用する必要がありますか?

4

3 に答える 3

3

実際に受け入れられる構文atomic updateは次のとおりです。

x ++;

x- ;

++ x ;

--x ; _

x binop = expr ;

x = x binop expr ;

ここで、xはスカラーl値式であり、exprは関数呼び出しを含む任意の式ですが、唯一の制限はスカラー型でなければならないということです。コンパイラーは、中間結果を一時変数に格納するように注意します。

インターネットでチュートリアルを読むよりも、標準ドキュメントを参照する方がよい場合があります。OpenMP3.1標準の例A.22.1cを確認してください。

float work1(int i)
{
  return 1.0 * i;
}
...
#pragma omp atomic update
x[index[i]] += work1(i);
于 2012-12-13T11:51:08.763 に答える
1

また、ifブロックがクリティカルセクションである場合は、書き込みをシリアル化せずに複数のスレッドからベクターを書き込むべきではないと思います。を使用#pragma omp criticalして、ifブロックの+=の実行を一度に1つのスレッドに制限できます。

于 2012-12-13T10:26:58.093 に答える
1

式を2つの部分に分割できます。結果を一時に割り当てる関数呼び出しと、その一時をアキュムレータに追加する関数呼び出しです。omp atomicその場合、加算自体がカスタム型の複雑なオーバーロード演算子ではないと仮定すると、2番目の式は使用するのに十分単純になります。もちろんG->weight(u,v)、スレッドセーフな呼び出しである場合にのみそれを行うことができます。そうでない場合は、omp criticalまたはミューテックスを使用する必要があります。

于 2012-12-13T10:57:45.503 に答える