Разбросать массив только по рабочим задачам

У меня есть массив, в котором хранятся массивы (называемые sendbuff), и я хотел бы отправить эти массивы в другие потоки с помощью MPI :: Scatter.

                        sendbuff
#####    ###############################
p  # 0 # -> # -1 # -1 # -1 # -1 # -1 # -1 # (values)
o  #####    ###############################
s  # 1 # -> # -1 # -1 # -1 # -1 # -1 # -1 # (values)
#####    ###############################

Как видно, sendbuff[0] содержит массив размером 6, который имеет 6 значений (все -1) и sendbuff[1] имеет то же самое. Я хочу отправить эти массивы -1 другим потокам и сохранить их в массиве с именем recvbuff, который заполнен нулями:

        recvbuff
#########################
# 0 # 0 # 0 # 0 # 0 # 0 #
#########################

Я исследовал ОС для ответов и нашел некоторые, но они используют MPI_Datatype, но я бы хотел этого избежать.
Чтобы попытаться достичь этой цели, я создал следующий код, который не работает:

int main( int argc, char *argv[]){

//variable innitialization
int taskid, ntasks, buffsize, **sendbuff, *recvbuff;

MPI::Init(argc, argv);

taskid = MPI::COMM_WORLD.Get_rank();
ntasks = MPI::COMM_WORLD.Get_size();

buffsize = 6;

//memory innitialization
recvbuff = new int[buffsize];
sendbuff = new int*[ntasks];
for(int i = 0; i < ntasks; i++){
sendbuff[i] = new int[buffsize];
}

//array innitialization
for(int i = 0; i < buffsize; i++){
recvbuff[i] = 0;
}
for(int i = 0; i < ntasks; i++){
for(int j = 0; j < buffsize; j++){
sendbuff[i][j] = -1;
}
}

//communication
MPI::COMM_WORLD.Scatter(sendbuff[0], buffsize, MPI::INT, recvbuff, buffsize,
MPI::INT, 0);

//output
for(int i = 0; i < buffsize; i++){
cout<<"Task"<<taskid<<" recvbuff["<<i<<"] = "<<recvbuff[i] << endl;
}

//cleaning
for(int i = 0; i < ntasks; i++){
delete[] sendbuff[i];
}
delete[] sendbuff;
delete[] recvbuff;MPI::Finalize();

return EXIT_SUCCESS;
}

После использования Scatter, я ожидал, что он recvbuff переменная должна быть заполнена значениями -1, однако я получаю смесь -1 и мусора следующим образом:

$ mpirun -np 3 a.out
Task0 recvbuff[0] = -1
Task0 recvbuff[1] = -1
Task0 recvbuff[2] = -1
Task0 recvbuff[3] = -1
Task0 recvbuff[4] = -1
Task0 recvbuff[5] = -1
Task1 recvbuff[0] = 33
Task1 recvbuff[1] = 0
Task1 recvbuff[2] = -1
Task1 recvbuff[3] = -1
Task1 recvbuff[4] = -1
Task1 recvbuff[5] = -1
Task2 recvbuff[0] = -1
Task2 recvbuff[1] = -1
Task2 recvbuff[2] = 33
Task2 recvbuff[3] = 0
Task2 recvbuff[4] = 1768975727
Task2 recvbuff[5] = 7496543

Что я делаю не так?
Заранее спасибо, Педро.

1

Решение

Разберись и соберись подробно описаны в этом ответе. 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; то есть разброс идет все процессы в коммуникаторе. Если вы пытаетесь отправить свои задачи не с нулевым рангом, самое простое, что нужно сделать, это просто сохранить ряд фиктивных данных в sendbuff для ранга 0, чтобы нормальный разброс работал правильно:

#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

Если вы предпочитаете не использовать фиктивные данные для ранга 0 (возможно, большого размера), вы можете разбить задачи на две группы: основная задача и рабочие задачи и настроить интеркоммуникатором это позволяет коллективные связи между ними. Вот простая программа, которая делает именно это:

#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

С другой стороны, если вы не хотите возиться с интеркоммуникаторами, просто сохраните ряд фиктивных данных в sendbuff для ранга 0, чтобы нормальный разброс работал правильно:

#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
1

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]