2017-05-20 1 views
1

私は自分自身にいくつかのOpenMPを教えました。これはばかげているかもしれません。基本的に私は、各ノードが処理に長い時間を費やしながら、幅広い最初の検索プログラムをC++で並列化しようとしています。ここにコード例があります:forループで1つの関数だけを並列化するようにOpenMPに指示するにはどうすればよいですか?

queue<node*> q; 
q.push(head); 
while (!q.empty()) { 
    qSize = q.size(); 
    for (int i = 0; i < qSize; i++) { 
    node* currNode = q.front(); 
    q.pop(); 
    doStuff(currNode); 
    q.push(currNode); 
    } 
} 

処理関数doStuff()は非常に高価で、並列化したいと思います。しかし、forループの直前に#pragma omp parallel forを置くことによって、forループを並列化すると、実行時にあらゆる種類の奇妙なエラーがポップアップします。私はその理由は、q.front()q.push()が並列化され、q.front()によって複数のスレッドが同じノードを取得する可能性があることを推測しています(q.pushが処理される前にすべて処理されているためです)。

どうすればこの問題を回避できますか?

+0

幅優先検索は、本質的には順次アルゴリズムです。これは、各ステップが前のステップの結果に依存するためです。それを並列化するためのいくつかのテクニックがあります - "並列木越え"のためのウェブ検索を行いますが、それはOpenMPの 'parallel for'ほどシンプルではありません。 – Wyzard

+0

@WyzardあなたはDFSとBFSを混同しなければなりません。並列でBFSの前面を計算するだけでも問題ありません。 – Zulan

答えて

3

解決方法は、クリティカルセクションでキューへのアクセスを保護することです。

queue<node*> q; 
q.push(head); 
while (!q.empty()) { 
    qSize = q.size(); 
    #pragma omp parallel for 
    for (int i = 0; i < qSize; i++) { 
    node* currNode; 
    #pragma omp critical 
    { 
     currNode = q.front(); 
     q.pop(); 
    } 
    doStuff(currNode); 
    #pragma omp critical 
    q.push(currNode); 
    } 
} 

これは、共通のミューテックスを使用してロックするのと同じです。

このバージョンの効率にはいくつかの制限があります。forループの最後では、キュー内の作業にもかかわらず、一部のスレッドがアイドル状態になることがあります。キューに何かがあるときにスレッドが連続して動作するバージョンを作ることは、キューが空であるが、まだいくつかのスレッドが計算している状況を扱うという点で少し難解です。

ノードに関連するデータ・サイズに応じて、キャッシュ効果と誤った共有のパフォーマンスに大きな影響を与える可能性があります。しかし、それは具体的な例で議論することはできません。単純なバージョンはおそらく多くの場合十分に効率的ですが、最適なパフォーマンスを得ることは任意に複雑になる可能性があります。

いずれの場合でも、doStuffがグローバルまたは共有状態を変更しないようにする必要があります。

+0

クリティカルセクションに 'q.push(currNode)'を入れる必要がありますか?このforループは、各レベルの始めに決定されたキューのサイズと同じ回数だけ実行され、q.push()はキューの最後までstuffをプッシュします。どう思いますか? –

+0

**はい**。この場合、 'std :: queue'は並行データ構造ではないので、' q'へのすべてのアクセスを保護することが絶対に必要です。同時データ構造を検討する際に考える必要があることを垣間見るには、[この講演](https://www.youtube.com/watch?v=c1gO9aB9nbs)をご覧ください。 – Zulan

関連する問題