web-dev-qa-db-ja.com

MPI:ブロッキングvsノンブロッキング

MPIで通信のブロックと非ブロックの通信の概念を理解するのに苦労しています。 2つの違いは何ですか?長所と短所は何ですか?

57
lamba

通信のブロックは、 MPI_Send() および MPI_Recv() を使用して行われます。これらの関数は、通信が終了するまで戻りません(つまり、ブロックします)。多少簡略化すると、これは、MPIがどこかに保存したため、または宛先によって受信されたため、MPI_Send()に渡されたバッファーを再利用できることを意味します。同様に、MPI_Recv()有効なデータが入力されています。

対照的に、非ブロッキング通信は MPI_Isend() および MPI_Irecv() を使用して行われます。これらの関数は、通信がまだ終了していなくてもすぐに戻ります(つまり、ブロックしません)。 MPI_Wait() または MPI_Test() を呼び出して、通信が終了したかどうかを確認する必要があります。

ブロッキング通信は、使用しやすいため、十分な場合に使用されます。必要な場合は、ノンブロッキング通信が使用されます。たとえば、MPI_Isend()を呼び出し、いくつかの計算を行ってから、MPI_Wait()を実行できます。これにより、計算と通信がオーバーラップし、一般にパフォーマンスが向上します。

集合通信(例:all-reduce)は、MPIv2までのブロッキングバージョンでのみ利用可能です。 IIRC、MPIv3は、ノンブロッキングの集団通信を導入します。

MPIの送信モードの概要は、 こちら で確認できます。

79
user1202136

この投稿は少し古いですが、私は受け入れられた答えを主張します。 「通信が終了するまでこれらの関数は戻りません」というステートメントは、通信をブロックしても送受信操作でのハンドシェイクが保証されないため、少し誤解を招きます。

最初に知っておく必要があるのは、送信には4つのモードがあります通信:標準、バッファリング、同期および準備完了であり、これらはそれぞれblockingおよびnon-blocking

Sendとは異なり、receiveには1つのモードしかありませんであり、blockingまたはnon-blockingです。

さらに先に進む前に、どちらがMPI_Send\Recv bufferであり、どちらがsystem buffer(これが所有する各プロセッサーのローカルバッファーであるかを明示的に言及していることも明確にする必要がありますMPI通信グループのランク間でデータを移動するために使用されるライブラリ)

BLOCKING COMMUNICATION:ブロックは、メッセージが受信者/宛先に配信されたことを意味しません。これは、単に(送信または受信)バッファーが再利用できることを意味します。バッファを再利用するには、情報を別のメモリ領域にコピーするだけで十分です。つまり、ライブラリは、バッファデータをライブラリ内の独自のメモリ位置にコピーし、たとえばMPI_Sendが返すことができます。

MPI標準では、メッセージバッファリングを送信および受信操作から切り離すことが非常に明確になっています。一致する受信がポストされていなくても、メッセージがバッファリングされるとすぐにブロッキング送信が完了します。ただし、場合によっては、メッセージバッファリングが高価になる可能性があるため、送信バッファから受信バッファへの直接コピーが効率的な場合があります。したがって、MPI Standardは、アプリケーションに適切な送信モードを選択する自由をユーザーに与えるために、4つの異なる送信モードを提供します。各通信モードで何が起こるか見てみましょう。

1。標準モード

標準モードでは、MPIライブラリ、発信メッセージをバッファリングするかどうか。ライブラリが発信メッセージをバッファリングすることを決定した場合。ライブラリは、(パフォーマンス上の理由から、またはバッファスペースが利用できないために)バッファリングしないことを決定した場合、一致する受信がポストされるまで戻りません。そして、送信バッファ内のデータは受信バッファに移動されました。

標準モードのMPI_Sendは非ローカル一致する受信がポストされているかどうかに関係なく標準モードで送信を開始でき、その成功は一致する受信の発生に依存する可能性があるという意味で(メッセージがバッファリングされるかどうかは実装に依存するという事実のため)。

標準送信の構文は次のとおりです。

int MPI_Send(const void *buf, int count, MPI_Datatype datatype, 
             int dest, int tag, MPI_Comm comm)

2。バッファーモード

標準モードと同様に、バッファモードでの送信は、一致する受信がポストされ、一致する受信がポストされる前に送信が完了するという事実に関係なく開始できます。ただし、主な違いは、送信が開始され、一致する受信が送信されない場合、発信メッセージmustをバッファリングするという事実から生じます。一致する受信がポストされる場合、バッファされた送信は受信を開始したプロセッサとうまくランデブーできますが、受信がない場合、バッファモードの送信は送信メッセージをバッファリングして送信を完了させる必要があります。全体として、バッファされた送信はlocalです。この場合のバッファ割り当てはユーザー定義であり、バッファスペースが不十分な場合はエラーが発生します。

バッファ送信の構文:

int MPI_Bsend(const void *buf, int count, MPI_Datatype datatype,
             int dest, int tag, MPI_Comm comm)

。同期モード

同期送信モードでは、一致する受信がポストされたかどうかに関係なく、送信を開始できます。ただし、一致する受信がポストされ、受信者が同期送信によって送信されたメッセージの受信を開始した場合にのみ、送信は正常に完了します。同期送信の完了は、送信内のバッファを再利用できることだけでなく、受信プロセスがデータの受信を開始したことも示します。送信と受信の両方がブロックしている場合、通信プロセッサがランデブーする前に、通信はどちらの端でも完了しません。

同期送信の構文:

int MPI_Ssend(const void *buf, int count, MPI_Datatype datatype, int dest,
              int tag, MPI_Comm comm)

4。準備完了モード

前の3つのモードとは異なり、準備完了モードでの送信は、一致する受信が既にポストされている場合にのみ開始できます。送信が完了しても、一致する受信については何も示されず、送信バッファーが再利用できることを示すだけです。準備完了モードを使用する送信には、標準モードまたは同期モードと同じセマンティクスがあり、一致する受信に関する追加情報があります。通信の準備完了モードを備えた正しいプログラムは、パフォーマンスの違いを除き、結果に影響を与えない同期送信または標準送信に置き換えることができます。

すぐに送信できる構文:

int MPI_Rsend(const void *buf, int count, MPI_Datatype datatype, int dest, 
              int tag, MPI_Comm comm)

4つのすべてのブロッキング送信を経て、原則的に異なるように見えるかもしれませんが、実装によっては、1つのモードのセマンティクスが別のモードに似ている場合があります。

たとえば、一般的にMPI_Sendはブロッキングモードですが、実装に応じて、メッセージサイズが大きすぎない場合、MPI_Sendは送信メッセージを送信バッファーからシステムバッファーにコピーし(「これは現代のシステムではほとんどの場合)、すぐに戻ります」。以下の例を見てみましょう:

//assume there are 4 processors numbered from 0 to 3
if(rank==0){
    tag=2;
    MPI_Send(&send_buff1, 1, MPI_DOUBLE, 1, tag, MPI_COMM_WORLD);
    MPI_Send(&send_buff2, 1, MPI_DOUBLE, 2, tag, MPI_COMM_WORLD);
    MPI_Recv(&recv_buff1, MPI_FLOAT, 3, 5, MPI_COMM_WORLD);
    MPI_Recv(&recv_buff2, MPI_INT, 1, 10, MPI_COMM_WORLD);
}

else if(rank==1){
     tag = 10;
    //receive statement missing, nothing received from proc 0
    MPI_Send(&send_buff3, 1, MPI_INT, 0, tag, MPI_COMM_WORLD);
    MPI_Send(&send_buff3, 1, MPI_INT, 3, tag, MPI_COMM_WORLD);
}

else if(rank==2){
    MPI_Recv(&recv_buff, 1, MPI_DOUBLE, 0, 2, MPI_COMM_WORLD);
    //do something with receive buffer
}

else{ //if rank == 3
    MPI_Send(send_buff, 1, MPI_FLOAT, 0, 5, MPI_COMM_WORLD);
    MPI_Recv(recv_buff, 1, MPI_INT, 1, 10, MPI_COMM_WORLD);
}

上記の例の各ランクで何が起こっているか見てみましょう

ランクは、ランク1およびランク2に送信し、ランク1およびランク3から受信しようとしています。

ランク1はランク0およびランク3に送信しようとし、他のランクからは何も受信しようとしない

ランク2はランク0から受信しようとしており、後でrecv_buffで受信したデータを使用して何らかの操作を行います。

ランクはランク0に送信し、ランク1から受信しようとしています

初心者が混乱するのは、ランク0がランク1に送信しているが、ランク1が受信操作を開始していないため、通信shouldブロックまたはストールとランク0の2番目のsendステートメントはまったく実行しないでください(これはMPIドキュメンテーションは、送信メッセージがバッファリングされるかどうかにかかわらず実装が定義されることを強調しています)。現代のシステムでは、このような小さなサイズ(ここではサイズ1)のメッセージは簡単にバッファリングされ、MPI_Sendは次のMPI_Sendステートメントを返して実行します。したがって、上記の例では、ランク1の受信が開始されていなくても、ランクの最初のMPI_Send 0が返され、次のステートメントが実行されます。

ランク3がランク0よりも前に実行を開始するという仮想の状況では、最初の送信ステートメントの送信メッセージを送信バッファーからシステムバッファーにコピーし(最新のシステムでは;))、受信ステートメントの実行を開始します。ランク0が2つの送信ステートメントを終了し、受信ステートメントの実行を開始するとすぐに、ランク3によってシステムにバッファリングされたデータがランク0の受信バッファにコピーされます。

プロセッサで受信操作が開始され、一致する送信がポストされない場合、受信バッファが期待するデータで満たされるまでプロセスはブロックされます。この状況では、MPI_Recvが返されない限り、計算または他のMPI通信はブロック/停止されます。

バッファリング現象を理解したので、戻って、ブロッキング通信の真の意味を持つMPI_Ssendについてもっと考えるべきです。 MPI_Ssendが送信バッファーから送信バッファーをシステムバッファー(これも実装定義です)にコピーする場合でも、受信プロセスからの確認応答(低レベル形式)が送信プロセッサーによって受信されない限り、MPI_Ssendは返されないことに注意する必要があります。

幸いなことにMPI受信とブロッキング通信には受信が1つしかありません:MPI_Recvのいずれかで使用することができます。上記の4つの送信モード.MPI_Recvの場合、ブロッキング手段バッファにデータが含まれた後にのみ受信が戻ることは、一致する送信が開始された後にのみ受信が完了できることを意味しますが、一致する送信が完了する前に完了するかどうか。

このようなブロッキング呼び出し中に起こることは、ブロックされたバッファが解放されるまで計算が停止することです。通常、Send/Recvは1つのメモリ位置から別のメモリ位置にデータをコピーしますが、CPUのレジスタはアイドルのままであるため、通常は計算リソースの浪費につながります。

NON-BLOCKING COMMUNICATION:非ブロッキング通信の場合、アプリケーションは送信および/または受信の通信要求を作成し、ハンドルを取得して終了します。プロセスが実行されることを保証するために必要なのはそれだけです。つまり、MPIライブラリは、操作を実行する必要があることを通知されます。

送信側の場合、これにより、計算と通信を重複させることができます。

これにより、受信側では、通信オーバーヘッドの一部をオーバーラップできます。つまり、アプリケーションの受信側のアドレス空間にメッセージを直接コピーできます。

32
ggulgulia

ブロッキング通信を使用する場合、たとえばこのコードを見て、呼び出しの送受信に注意する必要があります

 if(rank==0)
 {
     MPI_Send(x to process 1)
     MPI_Recv(y from process 1)
 }
 if(rank==1)
 {
     MPI_Send(y to process 0);
     MPI_Recv(x from process 0);
 }

この場合はどうなりますか?

  1. プロセス0はxをプロセス1に送信し、プロセス1がxを受信するまでブロックします。
  2. プロセス1はyをプロセス0に送信し、プロセス0がyを受信するまでブロックしますが、
  3. プロセス0はブロックされ、プロセス1は2つのプロセスが強制終了されるまで無限にブロックされます。
10
peaceman

容易いものだ。

ノンブロッキングとは、1つのプロセスでデータの計算と転送が同時に行われることを意味します。

ブロッキングとは、バディを意味しますが、データの転送が完了していることを確認してから、次のコマンドを完了するために戻らなければなりません。