私が見る限り、これはマスターノードのwhileループのrecvに起因します。
...
while (1) {
// Receive results from slave.
stat = world.recv(MPI_ANY_SOURCE,MPI_ANY_TAG);
...
1つのスレーブからのメッセージがある場合、マスターノードは並列で実行されていないため、whileループ内のコードが終了するまで(スリープがあるため、しばらく時間がかかります)、マスターはメッセージを取得できません。したがって、他のすべてのスレーブは、最初のスレーブがメッセージの送信を終了するまで、メッセージの送信を開始できません。次に、次のスレーブはメッセージの送信を開始できますが、whileループ内のコードが実行されるまで、他のすべてのスレーブは停止されます。
これにより、スレーブの通信が非常に遅いという動作が発生します。この問題を回避するには、ポイントツーポイント通信をノンブロッキングで実装するか、グローバル通信を使用する必要があります。
更新1:
マスターが自分のデータを配布したと仮定しましょう。今、彼は奴隷が報告するまで待ちます。最初のスレーブが報告すると、最初にREPORTTAGを送信し、次にDONETAGを送信します。今、マスターは彼に新しい仕事を送り返します
currentTask < numtasks
今、奴隷は彼の計算から再び始めます。彼が終了するまで、マスターは別のスレーブしか処理できなかったのかもしれません。したがって、最初のスレーブは、最初にREPORTTAGを送信し、次にDONETAGを送信して、新しいジョブを取得します。これが最終的に続くとき、2人の奴隷だけが新しい仕事を得て、残りは彼らの仕事を終えることができませんでした。したがって、ある時点でこれは真実です。
currentTask >= numtasks
これで、すべてのスレーブがデータを報告し、複数のタスクを実行したわけではなくても、すべてのジョブを停止できます。
この問題は、異なるノードのネットワーク接続が大きく異なる場合に最も発生します。その理由は、送信と受信は呼び出し後に処理されず、代わりに、これらの機能の2つが何らかのハンドシェイクを実行できる場合に通信が行われるためです。
解決策として、私は次のいずれかを提案します:
- すべてのジョブを強制終了する前に、すべてのスレーブが終了していることを確認してください
- メッセージの代わりに収集と分散を使用すると、各タスクの後にすべてのスレーブが同期されます。
- メッセージがそれほど大きくない場合は、バッファリングされた、またはバッファリングされていない送信および受信操作を使用します。マスターでメモリオーバーフローが発生していないことを確認してください
- マスター/スレーブからより並列なワークモードに変更します。たとえば、すべてのタスクを2つのノードに分割し、次にこれらのノードから次の2つのノードにタスクをさらに分割します。最後に、この方法でタスクを送り返します。このソリューションには、通信コストがO(n)ではなくO(logn)のみであるという利点もあります。
これがお役に立てば幸いです。