5

特定のシステムのシミュレーションを実行するプログラムを C++ で作成しています。タイムステップごとに、実行の大部分は 1 つのループで占められています。幸いなことに、これは非常に並列であるため、Boost Threads を使用して並列化することにしました (2 コアのマシンで実行しています)。ロックがないため、シリアル バージョンの 2 倍近くの高速化が期待できます。ただし、スピードアップはまったくないことがわかりました。

ループの並列バージョンを次のように実装しました。

  • 2 つのスレッドを起動します (バリアでブロックされています)。
  • その後、各スレッドは以下を実行します。

    • グローバル カウンターをアトミックにフェッチしてインクリメントします。
    • そのインデックスでパーティクルを取得します。
    • その粒子で計算を実行し、結果を別の配列に格納します
    • ジョブ終了バリアで待機
  • メイン スレッドはジョブ終了バリアで待機します。

このアプローチを使用したのは、適切な負荷分散を提供する必要があるためです (各計算にかかる時間が異なる場合があるため)。この速度低下の原因は何なのか、非常に興味があります。アトミック変数は高速であるといつも読んでいますが、今ではパフォーマンス コストがあるのか​​どうか疑問に思い始めています。

何を探すべきか、またはヒントがあれば、本当に感謝します。私は 1 週間頭を悩ませてきましたが、プロファイリングではあまり明らかになりませんでした。

編集:問題は解決しました! この問題をどのように解決したかを詳しく説明します。再度 gprof を使用しましたが、今回は最適化フラグ (-O3) なしでコンパイルしました。すぐに、プロファイラーは、個々の粒子の計算を実行する関数に信じられないほどの時間を費やしていることを示しました。これは、シリアル バージョンよりもはるかに長い時間です。

この関数は仮想であり、多態的にアクセスされます。vtable を介してではなく直接アクセスするようにコードを変更したところ、ほら、並列バージョンではほぼ 2 倍のスピードアップが実現しました。シリアル版での同じ変更はほとんど効果がありませんでした。

なぜそうなのかはよくわかりませんが、誰かが知っていれば興味があります!

ポスターの皆様ありがとうございました。皆さんはある程度助けてくれました。1 つの答えを受け入れるのは非常に難しいでしょう。

4

5 に答える 5

2

その粒子で計算を実行し、結果を別の配列に格納します

計算はどれくらい重いですか?

  • 一般的に言えば、アトミック カウンターは何百ものクロック サイクルを必要とする場合があり、カウンターをインクリメントするだけではないことを確認することが非常に重要です。
  • また、各スレッドがどの程度のジョブを実行するかを確認してみてください - それらはうまく連携しますか (つまり、サイクルでそれぞれが粒子の約半分を進めます)。
  • ジョブを単一のパーティクルよりも大きなチャンク (100 個のパーティクルなど) に分割してみてください。
  • スレッドの外でどれだけの仕事が行われているかを確認してください。

正直なところ...あなたが話しているのはバグのようです。

于 2010-04-14T11:06:47.900 に答える
1

profiling has not revealed much

これは不明です。HP-UX でマルチスレッド アプリケーションのプロファイリングを行った経験があり、そのプロファイラーには、各関数が実行される時間の割合が表示されます。したがって、関数に 1 つまたはいくつかの競合ポイントがある場合、アプリケーションがこれらの関数に費やす時間が増加します。私の場合、 が大幅に増加しましたpthread_mutex_unlock()。コードを変更すると、はるかに高速になりました。

1 つのスレッドと 2 つまたは 4 つのスレッドの同じ統計をここに投稿できますか。そして、各テストの計算数。

また、(可能であれば) ミューテックスをロックするグローバル関数にブレークポイントを設定することをお勧めします。アルゴリズムのどこかで、偶然にもグローバルミューテックスをロックしていることに気付くかもしれません。

于 2010-04-14T11:24:26.380 に答える
1

あなたの言語は一種の暴露です:

xxx待って

これはあなたの問題かもしれません。


さらに、単一の結果キューに再度追加すると遅くなります。可能であれば、処理の最後にのみ結果を単一のキューに追加することもできます。メイン スレッドは待機しないでください。更新のたびにグローバル カウンターを確認してください。
プロファイリングの代わりに、最後に記録するパフォーマンス カウンターを追加します。それらを条件付きコンパイル エラーに入れて、本番コードに追加されないようにすることができます。

于 2010-04-14T11:54:54.980 に答える
0

あなたは、プロファイリングではあまり明らかにされていないとおっしゃっていますが、それは (悲しいことに) 典型的なことです。

これが私がすることです:

  1. シングルスレッドに戻ります。

  2. どの言語や環境でも機能するこのプロファイリング手法を使用して、単一スレッドを可能な限り高速にします。その理由は、プロファイラー (すべてではありませんがほとんど) は、修正すべき点を特定するのではなく、変更を測定することだけが得意だからです。

  3. 次に、コアあたり 1 スレッドに戻り、プロセスをもう一度実行します。いずれかのスレッドがプロセス間通信でブロックされて多くの時間を費やしていることがわかった場合は、それをやり直す必要があります。

于 2010-04-14T11:30:24.033 に答える
0

この種の並列処理では、OpenMP の方が簡単だと思うかもしれません。ループを並列にしたいだけなので、スレッドを明示的に操作する必要はありません。これはまさに、OMP が本当に効果的な種類のものです。

とにかく一見の価値があります。

于 2010-04-14T12:04:24.613 に答える