通常、これはデータの再配布と呼ばれていますが、再配布する場合は、タスク間の均等性のようなメトリックの下で配布が最適であることを理解しています。
これは、計算負荷分散を実行しようとしているときに、科学技術計算に現れます。いくつかの次元で計算を行っていても、スペース・フィリング・カーブでプロセッサーに割り当てた空間データを再配分すると、この正確な問題が発生し、データを均等に分割したいことがあります。
この手順はかなり簡単です。あなたはxiの排他的なprefix sumを取って、あなたの "左"にいくつのアイテムがあるかを知ることから始めます。例えば、上記のNoxvilleの例えば、あなたが持っていた場合、データは
[9, 6, 1, 6, 2]
プレフィックス和が
[0, 9, 15, 16, 22]
だろうし、そこにあることを、あなたは(最後のプロセッサの合計からプラス、それが持っているどのように多く)見つけるだろう合計24項目。
次に、理想的なパーティションの大きさ、たとえばceil(totitems/nprocs)を計算します。しかし、すべてのプロセッサがすべてのパーティションサイズに同意する限り、これを行うことができます。
今、いくつかの方法があります。データ項目がある意味で大きく、メモリ内に2つのコピーを持つことができない場合、データを最寄りのものだけにシフトすることができます。あなたはあなたの左のアイテムの数とその方向の「過剰」または「赤字」を知っています。あなたはまた、あなたがどれくらい多くいるかを知っています(あなたが過不足を解消するためにあなたの仕事をした後に持っていくでしょう)。したがって、左と右のネイバーにデータを送信し、左と右のネイバーからデータを受け取るようになります。プロセッサーが左いっぱいにまとめて適切な量のアイテムを持ち、あなたも同様に処理します。
しかし、データのコピーを2つ用意することができれば、送信されるメッセージの数を最小限に抑える別の方法をとることができます。左のセルの数を、ローカルデータの開始インデックスとして「グローバル」配列に考えることができます。各プロセッサがどれだけ多くのアイテムを処理するのか分かっているので、それらのアイテムがどのプロセスで終了するのかを直接把握し、直接送信することができます。 (例えば、上記の例では、0..8のアイテムを持つプロセッサ0は、最後のプロセッサが5つのデータアイテムで終わる場合、5-8の値をプロセッサ1に送ることができることを知っています。 )それらが送信されると、期待している量のデータがあるまで受信します。あなたは終わった。
以下はCとMPIでこれを行う簡単な例ですが、基本的なアプローチはどこでもうまくいくはずです。 MPIの接頭辞スキャン操作は、包括合計を生成し、私たちは排他的加算取得する値の私たち自身の数を差し引く必要があります:
#include <stdio.h>
#include <stdlib.h>
#include <mpi.h>
#include <time.h>
void initdata(const int rank, const int maxvals, char **data, int *nvals) {
time_t t;
unsigned seed;
t = time(NULL);
seed = (unsigned)(t * (rank + 1));
srand(seed);
*nvals = (rand() % (maxvals-1)) + 1;
*data = malloc((*nvals+1) * sizeof(char));
for (int i=0; i<*nvals; i++) {
(*data)[i] = 'A' + (rank % 26);
}
(*data)[*nvals] = '\0';
}
int assignrank(const int globalid, const int totvals, const int size) {
int nvalsperrank = (totvals + size - 1)/size;
return (globalid/nvalsperrank);
}
void redistribute(char **data, const int totvals, const int curvals, const int globalstart,
const int rank, const int size, int *newnvals) {
const int stag = 1;
int nvalsperrank = (totvals + size - 1)/size;
*newnvals = nvalsperrank;
if (rank == size-1) *newnvals = totvals - (size-1)*nvalsperrank;
char *newdata = malloc((*newnvals+1) * sizeof(char));
newdata[(*newnvals)] = '\0';
MPI_Request requests[curvals];
int nmsgs=0;
/* figure out whose data we have, redistribute it */
int start=0;
int newrank = assignrank(globalstart, totvals, size);
for (int val=1; val<curvals; val++) {
int nextrank = assignrank(globalstart+val, totvals, size);
if (nextrank != newrank) {
MPI_Isend(&((*data)[start]), (val-1)-start+1, MPI_CHAR, newrank, stag, MPI_COMM_WORLD, &(requests[nmsgs]));
nmsgs++;
start = val;
newrank = nextrank;
}
}
MPI_Isend(&((*data)[start]), curvals-start, MPI_CHAR, newrank, stag, MPI_COMM_WORLD, &(requests[nmsgs]));
nmsgs++;
/* now receive all of our data */
int newvalssofar= 0;
int count;
MPI_Status status;
while (newvalssofar != *newnvals) {
MPI_Recv(&(newdata[newvalssofar]), *newnvals - newvalssofar, MPI_CHAR, MPI_ANY_SOURCE, stag, MPI_COMM_WORLD, &status);
MPI_Get_count(&status, MPI_CHAR, &count);
newvalssofar += count;
}
/* wait until all of our sends have been received */
MPI_Status statuses[curvals];
MPI_Waitall(nmsgs, requests, statuses);
/* now we can get rid of data and relace it with newdata */
free(*data);
*data = newdata;
}
int main(int argc, char **argv) {
const int maxvals=30;
int size, rank;
char *data;
int mycurnvals, mylvals, myfinalnvals;
int totvals;
MPI_Init(&argc, &argv);
MPI_Comm_size(MPI_COMM_WORLD, &size);
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
initdata(rank, maxvals, &data, &mycurnvals);
MPI_Scan(&mycurnvals, &mylvals, 1, MPI_INT, MPI_SUM, MPI_COMM_WORLD);
if (rank == size-1) totvals = mylvals;
mylvals -= mycurnvals;
MPI_Bcast(&totvals, 1, MPI_INT, size-1, MPI_COMM_WORLD);
printf("%3d : %s %d\n", rank, data, mylvals);
redistribute(&data, totvals, mycurnvals, mylvals, rank, size, &myfinalnvals);
printf("%3d after: %s\n", rank, data);
free(data);
MPI_Finalize();
return 0;
}
これはあなたが予想される動作を取得し実行します。最終的なプロセッサは一般的にアンダーロードされることに注意してください(「ceil(totvals/nprocesses)」を使用して)「望ましい」パーティショニングを決定しました。
$ mpirun -np 13 ./distribute
0 : AAAAAAAAAAA 0
1 : BBBBBBBBBBBB 11
2 : CCCCCCCCCCCCCCCCCCCCCCCCCC 23
3 : DDDDDDD 49
4 : EEEEEEEEE 56
5 : FFFFFFFFFFFFFFFFFF 65
6 : G 83
7 : HHHHHHH 84
8 : IIIIIIIIIIIIIIIIIIIII 91
9 : JJJJJJJJJJJJJJJJJJ 112
10 : KKKKKKKKKKKKKKKKKKKK 130
11 : LLLLLLLLLLLLLLLLLLLLLLLLLLLL 150
12 : MMMMMMMMMMMMMMMMMM 178
0 after: AAAAAAAAAAABBBBB
1 after: BBBBBBBCCCCCCCCC
2 after: CCCCCCCCCCCCCCCC
3 after: DDDDDDDCEEEEEEEE
4 after: EFFFFFFFFFFFFFFF
5 after: FFFHHHHHHHIIIIIG
6 after: IIIIIIIIIIIIIIII
7 after: JJJJJJJJJJJJJJJJ
8 after: JJKKKKKKKKKKKKKK
9 after: LLLLLLLLLLKKKKKK
10 after: LLLLLLLLLLLLLLLL
11 after: LLMMMMMMMMMMMMMM
12 after: MMMM
ご質問がunderspecifiedされます。また、私は(それが十分順序が重要な場合は行うのは簡単だが)再配布に保存されている順番を確保しようとする試みを作っていませんでした。例えば、1ダースの遠隔ノードは、コスト1で近くの隣人と同時に話すことができますか? 100ノードにX宛てのメッセージがある場合、コスト1でそれらを一度に送信できますか、またはコスト100でシリアル化する必要がありますか?さまざまなアルゴリズムがさまざまな[計算のモデル]に適用されます(http://en.wikipedia.org/wiki/Models_of_computation)。具体的には、[ネットワークトポロジ](http://en.wikipedia.org/wiki/Network_topology)および/または分散メモリモデルを記述します。 –
それは実際には特定されていませんが、以下の回答のいくつかはすでに多くの助けを提供しています。 – Gus