2012-05-08 5 views
3

並列化したいシリアルC++プログラムがあります。私はMPIの基礎、MPI_SendMPI_Recvなどを知っています。基本的に、私はデータ処理アルゴリズムよりもはるかに高速に動作するデータ生成アルゴリズムを持っています。現在、それらはシリーズで実行されていますが、データ処理をルートプロセスで実行し、スレーブプロセスでデータ処理を行い、ルートから処理するデータを含むスレーブにメッセージを送信すると考えていました。このようにして、各スレーブはデータセットを処理し、次のデータセットを待つ。作業がなくなるとMPIスレーブプロセスがハングする

問題は、一旦ルートプロセスがデータを生成すると、スレーブがより多くを待っているのでプログラムがハングすることです。

これは問題の一例です:

#include "mpi.h" 

#include <cassert> 
#include <cstdio> 

class Generator { 
    public: 
    Generator(int min, int max) : value(min - 1), max(max) {} 
    bool NextValue() { 
     ++value; 
     return value < max; 
    } 
    int Value() { return value; } 
    private: 
    int value, max; 

    Generator() {} 
    Generator(const Generator &other) {} 
    Generator &operator=(const Generator &other) { return *this; } 
}; 

long fibonnaci(int n) { 
    assert(n > 0); 
    if (n == 1 || n == 2) return 1; 
    return fibonnaci(n-1) + fibonnaci(n-2); 
} 

int main(int argc, char **argv) { 
    MPI_Init(&argc, &argv); 

    int rank, num_procs; 
    MPI_Comm_rank(MPI_COMM_WORLD, &rank); 
    MPI_Comm_size(MPI_COMM_WORLD, &num_procs); 

    if (rank == 0) { 
    Generator generator(1, 2 * num_procs); 
    int proc = 1; 
    while (generator.NextValue()) { 
     int value = generator.Value(); 
     MPI_Send(&value, 1, MPI_INT, proc, 73, MPI_COMM_WORLD); 
     printf("** Sent %d to process %d.\n", value, proc); 
     proc = proc % (num_procs - 1) + 1; 
    } 
    } else { 
    while (true) { 
     int value; 
     MPI_Status status; 
     MPI_Recv(&value, 1, MPI_INT, 0, 73, MPI_COMM_WORLD, &status); 
     printf("** Received %d from process %d.\n", value, status.MPI_SOURCE); 
     printf("Process %d computed %d.\n", rank, fibonnaci(2 * (value + 10))); 
    } 
    } 

    MPI_Finalize(); 
    return 0; 
} 

明らかに上記ませんすべてが「良い習慣」ですが、ポイントを全体取得するのに十分です。

スレーブプロセスからwhile(true)を削除すると、各スレーブが終了したときにプログラムが終了します。私は、ルートプロセスがその仕事をし、すべてのスレーブが送信されたすべてを処理した後にのみ、プログラムを終了したいと思います。

生成されるデータセットの数がわかっていれば、多くのプロセスが実行され、すべてがうまく終了する可能性がありますが、ここでは該当しません。

提案がありますか?これを行うAPIがありますか?より良いトポロジでこれを改善できますか? MPI_IsendまたはMPI_IRecvはこれを改善しますか?私はMPIにはかなり新しいので、私に同行してください。通常の練習は、すべての作業者に送信することです

おかげ

+0

フィボナッチの実装はO(2^n)です。順序アルゴリズムを最適化する必要があります。 – mfontanini

+0

私は知っています。これは私が解決している実際の問題ではなく、問題をモデル化したと考えることができる単純な例です。 –

+0

多分私は何かが欠落していますが、各プロセスの終わりに単純な障壁があなたの問題を解決しないでしょうか? – suszterpatt

答えて

5

は無限処理ループを終了するには、それらを知らせる特別なタグを持つ空のメッセージを処理します。

while (true) { 
    int value; 
    MPI_Status status; 
    MPI_Recv(&value, 1, MPI_INT, 0, MPI_ANY_TAG, MPI_COMM_WORLD, &status); 
    if (status.MPI_TAG == 42) { 
    printf("Process %d exiting work loop.\n", rank); 
    break; 
    } 
    printf("** Received %d from process %d.\n", value, status.MPI_SOURCE); 
    printf("Process %d computed %d.\n", rank, fibonnaci(2 * (value + 10))); 
} 

マネージャのプロセスは、発電機のループの後にこのような何かをするでしょう:あなたの次の質問について

for (int i = 1; i < num_procs; i++) 
    MPI_Send(&i, 0, MPI_INT, i, 42, MPI_COMM_WORLD); 

のは、このタグは、あなたが労働者ループにそのようなことをするだろう42であるとしましょう。マスタープロセスでMPI_Isend()を使用すると、実行がデシリアライズされ、パフォーマンスが向上します。しかし実際には、あなたは非常に小さなメッセージを送信しており、それらは通常内部的にバッファされています(警告 - 実装に依存!)ので、MPI_Send()は実際にはブロックされておらず、 MPI_Isend()は、後で世話をする必要があるMPI_Requestハンドルを返します。 MPI_Wait()またはMPI_Waitall()で終了するまで待つことができますが、MPI_Request_free()を呼び出すだけで操作が終了すると自動的に解放されます。これは通常、多くのメッセージを非同期で送信したいときに行われ、送信が完了すると気にしませんが、大量の未処理要求が大量の貴重なメモリを消費する可能性があるため、悪い習慣です。ワーカープロセスについては、計算を進めるためにデータが必要なので、MPI_Irecv()を使用する必要はありません。

MPIプログラミングの素晴らしい世界へようこそ!

+0

これはまさに私が探しているものです。私はそのような方法でタグを使うことさえ考えなかった。かなりクール。小さなメッセージが出る限り、これはほんの単純化された例です。私が送信する実際のメッセージはずっと大きいので、MPI_SendとMPI_Isendで試して、どれが最高のパフォーマンスを発揮するか見てみましょう。ありがとう、私は助けに感謝します。 –

+1

ちょっとした注意 - 'MPI_Isend'は' MPI_Send'よりも速くメッセージを送信するのではなく、コミュニケーションと計算をオーバレイして前者のレイテンシーを隠すことができます。 –

+0

それを指摘してくれてありがとう。私はそれを認識していますが、MPIにはまだどちらが良いか知るのに十分な経験がありません。いくつかの実験のための時間。アドバイスをいただきありがとうございます。 –

関連する問題