0

私はopenMPを使用していくつかのステートメントを並列化しています。私はコンストラクトにパラレルを使用しています。並列化されたforループは次のようになります。

double solverFunction::apply(double* parameters_ , Model* varModel_)  
{
     double functionEvaluation = 0;
     Command* command_ = 0;
     Model* model_ = 0;

     #pragma omp parallel for  shared (functionEvaluation) private (model_,command_)
     for (int i=rowStart;i<rowEnd+1;i++)
     {
         model_ = new Model(varModel_);
         model_->addVariable("i", i);
         model_->addVariable("j", 1);
         command_ = formulaCommand->duplicate(model_);
         functionEvaluation += command_->execute().toDouble();
     }
}

それは平均してうまくいきます。実行時間が大幅に短縮され、期待どおりの結果が得られます。ただし、特に大きな問題(iでの反復回数が多い、コピーコンストラクター呼び出しでコピーするデータが多い)の場合は、時々発生します。

 model_ = new Model(varModel_);

、他?)、それは墜落した。コールスタックは、qAtomicBasic(C ++ / Qtで記述されたプログラム)、QHashなどのクラスで終了します。メモリ内の同時読み取り/書き込みアクセスが原因で、クラッシュする可能性があります。

ただし、model_とcommand_はプライベートであるため、各スレッドはそれぞれのコピーを処理します。変数model_で、varModel_をコピーして、引数で渡されたポインターがスレッドによって変更されないようにします。同様に、command_はメンバー変数formulaCommandのコピーです(複製は大まかにコピーコンストラクターです)。

私が特定したコードの考えられる欠陥は次のとおりです。

  • functionEvaluationは、複数のスレッドによって同時に変更される場合があります

  • ステートメント内のコンストラクターをコピーします

    model_ = new Model(varModel_);

メモリ内のvarModel_のメンバーを読み取り、新しい(model_)インスタンスを構築します。varModel_データメンバーへの同時アクセスが発生する可能性がありますが、これはここで値を変更することではなく、それらを読み取る(他の変数に影響を与える)だけです。

また、2つの改善のみが見られます(数日までテストできませんが、とにかくアドバイスを求めます)。

  • functionEvalutionが同時に書き込まれないようにアトミック句を追加します

  • functionEvaluationへのアクセスに関する同時実行性が自動的に処理されるように、演算子reduction(+、functionEvaluation)を追加します

これらの解決策は問題を正確に解決しているように見えますか?一般的にどちらがより効率的ですか?私が書いたコードのどこに問題があるのでしょうか?解決策は何ですか?

どうもありがとう!

4

2 に答える 2

1

最初の観察は、お気づきのように、functionEvaluation同時に変更することは悪い考えだということです。失敗します。

一方、の読み取り専用アクセスはvarModel_問題ありません。どちらもコピー コンストラクターの呼び出しではありません (ただし、どこにあるのでしょうか?コードには表示されません)。

これとは無関係に、privateC++ で句を使用するのは悪い考えです。並列ブロック (この場合はループ)でスレッド プライベート変数を宣言するだけです。for

ここでポインタを使用している理由もわかりません。それらを使用してもすぐには意味がありません。代わりに、スタック割り当てオブジェクトを使用してください。

次の変更されたコードは機能するはずです (コーディング スタイルを自由に統一することもできました...なぜ末尾のアンダースコアが?):

double solverFunction::apply(double parameters, Model const& varModel)
{
     double result = 0;

     #pragma omp parallel for reduction(+:result)
     for (int i = rowStart; i < rowEnd + 1; ++i)
     {
         Model model(varModel);
         mode.addVariable("i", i);
         mode.addVariable("j", i);
         Command command = formulaCommand->duplicate(model);
         result += command.execute().toDouble();
     }

     return result;
}

固有の浮動小数点の不正確さにより、このコードはシーケンシャル コードとは異なる結果になる可能性があることに注意してください。これは避けられません。

于 2012-04-14T19:49:29.967 に答える
0

同時に変更するfunctionEvaluationことは間違いなくコードの問題であり、それを処理する最善の方法はreduction句です。

さらなる問題は、並列呼び出しによってヒープ メモリを割り当てているという事実ですnew。これは、 への呼び出しにシステム全体のロックがあるため、多くの反復では決して良い考えではありませんnew。スタックは各スレッドにプライベートであり、ヒープは共有されるため、スタック割り当てに切り替えることを検討してください。

于 2012-04-14T19:59:18.230 に答える