スキャッターとギャザーは、この回答で詳細に説明されています。Scatterはデータを分割し、断片を他のタスクに分散しますが、データは連続したメモリに保存する必要があります-MPI_Scatterは、ポインターをたどる必要があること、もしそうなら、いくつあるか、そしてsendbuffを割り当てる方法を知る方法がありません:
sendbuff = new int*[ntasks];
for(int i = 0; i < ntasks; i++){
sendbuff[i] = new int[buffsize];
}
sendbuffのさまざまな行が、システムメモリ全体に散在している可能性があります。データを連続して割り当てると、ほぼそこにいます。
sendbuff = new int*[ntasks];
sendbuff[0] = new int[ntasks * 6];
for(int i = 1; i < ntasks; i++){
sendbuff[i] = &(sendbuff[0][i*6];
}
これで分散できるはずですが、行0がランク0になることに注意してください。つまり、スキャッターはコミュニケーター内のすべてのプロセスに行きます。ランク0以外のタスクに送信しようとしているだけの場合、最も簡単な方法は、通常のスキャッターが正しく機能するように、ランク0のsendbuffにダミーデータの行を保持することです。
#include <iostream>
#include <mpi.h>
int main(int argc, char **argv)
{
int rank, size;
const int nelem = 6;
MPI_Init(&argc, &argv);
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
MPI_Comm_size(MPI_COMM_WORLD, &size);
int **sendbuff = new int*[size];
int *recvbuff = new int[nelem];
if (rank == 0) {
sendbuff[0] = new int[nelem * size];
for (int i=0; i<size; i++)
sendbuff[i] = &(sendbuff[0][nelem*i]);
for (int i=0; i<size; i++)
for (int j=0; j<nelem; j++)
sendbuff[i][j] = i-1;
}
MPI_Scatter(sendbuff[0], nelem, MPI_INT, recvbuff, nelem, MPI_INT, 0, MPI_COMM_WORLD);
if (rank != 0) {
std::cout << "Scatter: [ " << rank << "]: ";
for (int i=0; i<nelem; i++)
std::cout << recvbuff[i] << " ";
std::cout << std::endl;
for (int i=0; i<nelem; i++)
recvbuff[i] *= recvbuff[i];
}
MPI_Gather(recvbuff, nelem, MPI_INT, sendbuff[0], nelem, MPI_INT, 0, MPI_COMM_WORLD);
if (rank == 0) {
for (int j=1; j<size; j++) {
std::cout << "Gather: [ " << j << "]: ";
for (int i=0; i<nelem; i++)
std::cout << sendbuff[j][i] << " ";
std::cout << std::endl;
}
}
delete [] recvbuff;
if (rank == 0)
delete [] sendbuff[0];
delete [] sendbuff;
MPI_Finalize();
}
データを分散させ、ワーカーが数値を2乗し、マスターがデータを収集していることに注意してください。コンパイルして実行すると、次のようになります。
$ mpic++ -o intercomm intercomm.cxx
$ mpirun -np 4 ./intercomm
Scatter: [ 2]: 1 1 1 1 1 1
Scatter: [ 1]: 0 0 0 0 0 0
Scatter: [ 3]: 2 2 2 2 2 2
Gather: [ 1]: 0 0 0 0 0 0
Gather: [ 2]: 1 1 1 1 1 1
Gather: [ 3]: 4 4 4 4 4 4
ランク0のダミーデータ(おそらく大きい)を避けたい場合は、タスクをマスタータスクとワーカータスクの2つのグループに分割し、それらの間の集合的な通信を可能にする相互通信機能を設定できます。これはまさにそれを行う簡単なプログラムです:
#include <iostream>
#include <mpi.h>
int main(int argc, char **argv)
{
MPI_Comm localComm; /* intra-communicator of local sub-group */
MPI_Comm interComm; /* inter-communicator */
int masterworker;
int rank, size;
const int nelem = 6;
int rootrank;
MPI_Init(&argc, &argv);
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
MPI_Comm_size(MPI_COMM_WORLD, &size);
masterworker = (rank == 0 ? 0 : 1);
MPI_Comm_split(MPI_COMM_WORLD, masterworker, rank, &localComm);
if (masterworker == 0)
{
MPI_Intercomm_create( localComm, 0, MPI_COMM_WORLD, 1, 1, &interComm);
rootrank = ( rank == 0 ? MPI_ROOT : MPI_PROC_NULL );
}
else {
MPI_Intercomm_create( localComm, 0, MPI_COMM_WORLD, 0, 1, &interComm);
rootrank = 0;
}
int **sendbuff = new int*[size-1];
int *recvbuff = new int[nelem];
if (rank == 0) {
sendbuff[0] = new int[nelem * (size-1)];
for (int i=1; i<size-1; i++)
sendbuff[i] = &(sendbuff[0][nelem*i]);
for (int i=0; i<size-1; i++)
for (int j=0; j<nelem; j++)
sendbuff[i][j] = i;
}
MPI_Scatter(sendbuff[0], nelem, MPI_INT, recvbuff, nelem, MPI_INT, rootrank, interComm);
if (masterworker == 1) {
std::cout << "Scatter: [ " << rank << "]: ";
for (int i=0; i<nelem; i++)
std::cout << recvbuff[i] << " ";
std::cout << std::endl;
for (int i=0; i<nelem; i++)
recvbuff[i] *= recvbuff[i];
}
MPI_Gather(recvbuff, nelem, MPI_INT, sendbuff[0], nelem, MPI_INT, rootrank, interComm);
if (masterworker == 0) {
for (int j=0; j<size-1; j++) {
std::cout << "Gather: [ " << j << "]: ";
for (int i=0; i<nelem; i++)
std::cout << sendbuff[j][i] << " ";
std::cout << std::endl;
}
}
MPI_Comm_free(&interComm);
MPI_Comm_free(&localComm);
delete [] recvbuff;
if (rank == 0)
delete [] sendbuff[0];
delete [] sendbuff;
MPI_Finalize();
}
繰り返しますが、コンパイルして実行すると、次のようになります。
$ mpic++ -o intercomm intercomm.cxx
$ mpirun -np 4 ./intercomm
Scatter: [ 1]: 0 0 0 0 0 0
Scatter: [ 2]: 1 1 1 1 1 1
Scatter: [ 3]: 2 2 2 2 2 2
Gather: [ 0]: 0 0 0 0 0 0
Gather: [ 1]: 1 1 1 1 1 1
Gather: [ 2]: 4 4 4 4 4 4
または、インターコミュニケーターをいじりたくない場合は、通常のスキャッターが正しく機能するように、ランク0のsendbuffにダミーデータの行を保持するだけです。
#include <iostream>
#include <mpi.h>
int main(int argc, char **argv)
{
int rank, size;
const int nelem = 6;
MPI_Init(&argc, &argv);
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
MPI_Comm_size(MPI_COMM_WORLD, &size);
int **sendbuff = new int*[size];
int *recvbuff = new int[nelem];
if (rank == 0) {
sendbuff[0] = new int[nelem * size];
for (int i=0; i<size; i++)
sendbuff[i] = &(sendbuff[0][nelem*i]);
for (int i=0; i<size; i++)
for (int j=0; j<nelem; j++)
sendbuff[i][j] = i-1;
}
MPI_Scatter(sendbuff[0], nelem, MPI_INT, recvbuff, nelem, MPI_INT, 0, MPI_COMM_WORLD);
if (rank != 0) {
std::cout << "Scatter: [ " << rank << "]: ";
for (int i=0; i<nelem; i++)
std::cout << recvbuff[i] << " ";
std::cout << std::endl;
for (int i=0; i<nelem; i++)
recvbuff[i] *= recvbuff[i];
}
MPI_Gather(recvbuff, nelem, MPI_INT, sendbuff[0], nelem, MPI_INT, 0, MPI_COMM_WORLD);
if (rank == 0) {
for (int j=1; j<size; j++) {
std::cout << "Gather: [ " << j << "]: ";
for (int i=0; i<nelem; i++)
std::cout << sendbuff[j][i] << " ";
std::cout << std::endl;
}
}
delete [] recvbuff;
if (rank == 0)
delete [] sendbuff[0];
delete [] sendbuff;
MPI_Finalize();
}
また、コンパイルして実行すると、次のようになります。
$ mpic++ -o intercomm intercomm.cxx
$ mpirun -np 4 ./intercomm
Scatter: [ 2]: 1 1 1 1 1 1
Scatter: [ 1]: 0 0 0 0 0 0
Scatter: [ 3]: 2 2 2 2 2 2
Gather: [ 1]: 0 0 0 0 0 0
Gather: [ 2]: 1 1 1 1 1 1
Gather: [ 3]: 4 4 4 4 4 4