私はISend()
2つの配列にしようとしています:とのサイズでarr1,arr2
ある整数. この投稿から、3つすべてを含む構造体を送信することはオプションではないことがわかりました。これは、実行時にのみ認識されるためです。明らかに、最初に受信する必要があります。そうしないと、受信プロセスが受信する要素の数がわからないためです。blokcing を使用せずにこれを達成する最も効率的な方法は何ですか?n
arr1,arr2
n
n
Send()
3 に答える
MPI は着信メッセージを受信せずにプローブする方法を提供するため、配列のサイズを送信することは冗長 (かつ非効率的) であり、メモリを適切に割り当てるのに十分な情報を提供します。プローブは で実行されMPI_PROBE
ます。これは によく似てMPI_RECV
いますが、バッファ関連の引数を取らない点が異なります。プローブ操作はステータス オブジェクトを返します。このオブジェクトは、メッセージの内容から抽出できる特定の MPI データ型の要素数を照会できますMPI_GET_COUNT
。したがって、要素数を明示的に送信することは冗長になります。
2 つのランクを使用した簡単な例を次に示します。
if (rank == 0)
{
MPI_Request req;
// Send a message to rank 1
MPI_Isend(arr1, n, MPI_DOUBLE, 1, 0, MPI_COMM_WORLD, &req);
// Do not forget to complete the request!
MPI_Wait(&req, MPI_STATUS_IGNORE);
}
else if (rank == 1)
{
MPI_Status status;
// Wait for a message from rank 0 with tag 0
MPI_Probe(0, 0, MPI_COMM_WORLD, &status);
// Find out the number of elements in the message -> size goes to "n"
MPI_Get_count(&status, MPI_DOUBLE, &n);
// Allocate memory
arr1 = malloc(n*sizeof(double));
// Receive the message. ignore the status
MPI_Recv(arr1, n, MPI_DOUBLE, 0, 0, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
}
MPI_PROBE
MPI_ANY_SOURCE
は、ワイルドカード ランクとワイルドカード タグも受け入れますMPI_ANY_TAG
。次に、実際の送信者ランクと実際のメッセージ タグを見つけるために、ステータス構造内の対応するエントリを調べることができます。
メッセージ サイズのプローブは、すべてのメッセージがエンベロープと呼ばれるヘッダーを運ぶときに機能します。エンベロープは、送信者のランク、受信者のランク、メッセージ タグ、およびコミュニケーターで構成されます。また、メッセージの合計サイズに関する情報も含まれます。エンベロープは、2 つの通信プロセス間の初期ハンドシェイクの一部として送信されます。
最初に、メモリ (フルメモリ = n = 要素) をランク 0 の arr1 と arr2 に割り当てる必要があります。つまり、フロントエンド プロセッサです。
番号に応じて配列をパーツに分割します。プロセッサの。各プロセッサの要素数を決定します。
この要素カウントをランク 0 から他のプロセッサに送信します。
2 番目の送信は、配列、つまり arr1 と arr2 に対するものです。
他のプロセッサでは、メイン プロセッサから受け取った要素数、つまりランク = 0 に従って arr1 と arr2 を割り当てます。要素数を受け取った後、割り当てられたメモリで 2 つの配列を受け取ります。
これは C++ 実装のサンプルですが、C も同じロジックに従います。また、Send と Isend を交換するだけです。
#include <mpi.h>
#include <iostream>
using namespace std;
int main(int argc, char*argv[])
{
MPI::Init (argc, argv);
int rank = MPI::COMM_WORLD.Get_rank();
int no_of_processors = MPI::COMM_WORLD.Get_size();
MPI::Status status;
double *arr1;
if (rank == 0)
{
// Setting some Random n
int n = 10;
arr1 = new double[n];
for(int i = 0; i < n; i++)
{
arr1[i] = i;
}
int part = n / no_of_processors;
int offset = n % no_of_processors;
// cout << part << "\t" << offset << endl;
for(int i = 1; i < no_of_processors; i++)
{
int start = i*part;
int end = start + part - 1;
if (i == (no_of_processors-1))
{
end += offset;
}
// cout << i << " Start: " << start << " END: " << end;
// Element_Count
int e_count = end - start + 1;
// cout << " e_count: " << e_count << endl;
// Sending
MPI::COMM_WORLD.Send(
&e_count,
1,
MPI::INT,
i,
0
);
// Sending Arr1
MPI::COMM_WORLD.Send(
(arr1+start),
e_count,
MPI::DOUBLE,
i,
1
);
}
}
else
{
// Element Count
int e_count;
// Receiving elements count
MPI::COMM_WORLD.Recv (
&e_count,
1,
MPI::INT,
0,
0,
status
);
arr1 = new double [e_count];
// Receiving FIrst Array
MPI::COMM_WORLD.Recv (
arr1,
e_count,
MPI::DOUBLE,
0,
1,
status
);
for(int i = 0; i < e_count; i++)
{
cout << arr1[i] << endl;
}
}
// if(rank == 0)
delete [] arr1;
MPI::Finalize();
return 0;
}
@Histro私が言いたいのは、Irecv/IsendはMPI libによって操作される関数自体であるということです。あなたが尋ねた質問は、送信/受信の後に何をするかについてのコードの残りの部分に完全に依存しています。2 つのケースがあります。
マスターとワーカー 問題の一部 (配列など) をワーカー (0=マスター以外のすべてのランク) に送信します。ワーカーは (配列に対して) いくつかの作業を行い、結果をマスターに返します。マスターは結果を集計し、新しい作業をワーカーに伝えます。ここで、マスターはすべてのワーカーが結果 (変更された配列) を返すまで待機する必要があります。したがって、Isend と Irecv を使用することはできませんが、私のコードと対応する recv で使用されている複数の送信を使用できます。コードがこの方向にある場合は、B_cast と MPI_Reduce を使用します。
怠惰なマスター マスターは作業を分割しますが、従業員の結果は気にしません。同じデータに対して異なる種類のパターンをプログラムしたいとします。ある都市の人口の特定の特性のように、18 歳以上の人が何人か、仕事を持っている人は何人か、ある会社で働いている人は何人かなどのパターンを計算したいとします。現在、これらの結果は互いに何の関係もありません。この場合、ワーカーがデータを受信するかどうかを気にする必要はありません。マスターは引き続き残りのコードを実行できます。これは、Isend/Irecv を安全に使用できる場所です。