2017-10-24 31 views
0

OpenMPタスク構造を使用して配列の要素の合計を見つけるために、次のコードを使用しています。
コードはn = 10000まで正しい結果を得ています。OpenMPタスク - より大きな反復回数〜10kでセグメント化エラーが発生するのはなぜですか?

それ以外では、セグメント違反が発生しています。 gdbを使用して、reduce()への再帰呼び出しの1つで障害が発生することがわかりました。入力配列の割り当てに問題はなく、私はそれを確認しました。

誰かが問題の原因について何か提案していますか?

int reduce (int *arr, unsigned long int n) 
{ 
    int x; 
    if (n <= 0) 
     return 0; 

    #pragma omp parallel 
    { 
     #pragma omp single nowait 
     { 
      #pragma omp task shared(x) 
      x = reduce(arr, n-1) + arr[n-1]; 
      #pragma omp taskwait 
     } 
    } 
    return x; 
} 
+3

'n 'が' unsigned'のときに '<0'になる方法はありますか? – yano

+0

スタックサイズを増やし、 'OMP_STACKSIZE'を見てください – Gilles

+0

コードを書式化してください。これはとても不必要に醜いです、それは痛いです。 – Zulan

答えて

2

関数呼び出しの再帰深度によって「スタックオーバーフロー」が発生しているようです。大部分のopenmpプラグマは関数自体を生成することに注意してください。おそらくtail-recursionの最適化が妨げられています。

valgrindを使用して実行する場合は、スタックのオーバーフローについて警告する必要があります。

+0

GCCまたはClangを使用している場合は、 '-fsanitize = address'をコンパイルしてリンクすることもできます。それはプログラムを装備し、valgrindよりも同様の問題をキャッチしますが、少し速くなります。 –

1

dlasalleは実際のエラーについては正しいです。

ただし、OpenMPタスクの使用方法に関する2つの基本的な問題があります。各再帰呼び出し内に並列領域を生成します。つまり、ネストした並列領域を使用します。デフォルトでは、ネストされた並列処理はOpenMPでは無効になっていますが、ここでは意味がありません。再帰中に生成するすべてのタスクを同じスレッドプールで実行する必要があります。これを行うには、parallel/singleを再帰の外側に移動する必要があります。

これがセグメンテーションフォールトではないだろう、とあなたは、コアの無限の量を持っていた場合でも、無限のメモリ帯域幅とノースレッド作成オーバーヘッドで、これはまだ並列化から任意のパフォーマンス上の利点を提供woudn't場合でも
int reduce_par(int* arr, unsigned long int n) 
{ 
    int x; 
    if (n <= 0) 
     return 0; 
    #pragma omp task shared(x) 
    x = reduce_par(arr, n - 1) + arr[n - 1]; 
    #pragma omp taskwait 
    return x; 
} 

int reduce(int* arr, unsigned long int n) 
{ 
    #pragma omp parallel 
    { 
     #pragma omp single nowait 
     { 
      reduce_par(arr, n); 
     } 
    } 
} 

。これを理解するには、タスクとその操作のグラフを描き、依存関係を追加します。タスクの依存関係を考慮した時間軸にグラフのノードを配置し、何かが並行して計算できるかどうかを確認してください。

並列和の正しい解は、reduce節を持つparallel forワークシェアリング構成です。タスクを使用する必要がある場合は、分割と征服を使用する必要があります。配列の2つの半分に対して2つのタスクを生成します。また、合理的なパフォーマンスを得るには、オーバーヘッドを管理しやすくするために、最小限のワークロード・サイズでタスクの作成/再帰を停止する必要があります。

関連する問題