Я пытаюсь написать решатель Пуассона для 3D параллельных вычислений с использованием OpenMPI версии 1.6.4.
Следующие части — мой код для параллельных вычислений, использующих блокировку отправки.
Следующая переменная объявлена в другом файле.
int px = lx*meshx; //which is meshing point in x axis.
int py = ly*meshy;
int pz = lz*meshz;
int L = px * py * pz
Следующий код работает хорошо, пока
lx=ly=lz=10;
meshx=meshy=2, meshz=any int number.
Не удалось отправить части recv, а meshx и meshy больше 4.
Программа висит там в ожидании отправки или получения данных.
Но это работает, если я только отправляю данные с одного процессора на другой, а не обмениваюсь данными.
(то есть: отправить с рангом от 0 до 1, но не от 1 до 0)
Я не могу понять, как работает этот код, в то время как meshx и meshy маленькие, но потерпели неудачу, в то время как номер сетки x y в большом.
Будет ли блокировать процесс отправки и приема прерываться сам или я путаю процессор в моем коде? Имеет ли это значение с размером моего массива?
#include "MPI-practice.h"
# include <iostream>
# include <math.h>
# include <string.h>
# include <time.h>
# include <sstream>
# include <string>
# include "mpi.h"
using namespace std;extern int px,py,pz;
extern int L;
extern double simTOL_phi;
extern vector<double> phi;
int main(int argc, char *argv[]){
int numtasks, taskid, offset_A, offset_B, DD_loop,s,e;
double errPhi(0),errPhi_sum(0);
MPI_Init(&argc, &argv);
MPI_Comm_size(MPI_COMM_WORLD, &numtasks);
MPI_Comm_rank(MPI_COMM_WORLD, &taskid);
MPI_Status status;
if((pz-1)%numtasks!=0){
//cerr << "can not properly divide meshing points."<<endl;
exit(0);
}
offset_A=(pz-1)/numtasks*px*py;
offset_B=((pz-1)/numtasks+1)*px*py;
s=offset_A*taskid;
e=offset_A*taskid+offset_B;int pz_offset_A=(pz-1)/numtasks;
int pz_offset_B=(pz-1)/numtasks+1;
stringstream name1;
string name2;
Setup_structure();
Initialize();
Build_structure();
if (taskid==0){
//master processor
ofstream output;
output.open("time", fstream::out | fstream::app);
output.precision(6);
clock_t start,end;
start=clock();
do{
errPhi_sum=0;
errPhi=Poisson_inner(taskid,numtasks,pz_offset_A,pz_offset_B);
//Right exchange
MPI_Send(&phi[e-px*py], px*py, MPI_DOUBLE, taskid+1, 1, MPI_COMM_WORLD);
MPI_Recv(&phi[e], px*py, MPI_DOUBLE, taskid+1, 1, MPI_COMM_WORLD, &status);
MPI_Allreduce ( &errPhi, &errPhi_sum, 1, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD );
}while(errPhi_sum>simTOL_phi);
end=clock();
output << "task "<< 0 <<" = "<< (end-start)/CLOCKS_PER_SEC <<endl<<endl;
Print_to_file("0.txt");
//recv from slave
for (int i=1;i<numtasks;i++){
MPI_Recv(&phi[offset_A*i], offset_B, MPI_DOUBLE, i, 1, MPI_COMM_WORLD, &status);
}
Print_to_file("sum.txt");
}
else{
//slave processor
do{
errPhi=Poisson_inner(taskid,numtasks,pz_offset_A,pz_offset_B);
//Left exchange
MPI_Send(&phi[s+px*py], px*py, MPI_DOUBLE, taskid-1, 1, MPI_COMM_WORLD);
MPI_Recv(&phi[s], px*py, MPI_DOUBLE, taskid-1, 1, MPI_COMM_WORLD, &status);//Right exchange
if(taskid!=numtasks-1){
MPI_Send(&phi[e-px*py], px*py, MPI_DOUBLE, taskid+1, 1, MPI_COMM_WORLD);
MPI_Recv(&phi[e], px*py, MPI_DOUBLE, taskid+1, 1, MPI_COMM_WORLD, &status);
}
MPI_Allreduce ( &errPhi, &errPhi_sum, 1, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD );
}while(errPhi_sum>simTOL_phi);
//send back master
MPI_Send(&phi[s], offset_B, MPI_DOUBLE, 0, 1, MPI_COMM_WORLD);
name1<<taskid<<".txt";
name2=name1.str();
Print_to_file(name2.c_str());}
MPI_Finalize();
}
Проблема в вашем самом внутреннем цикле. Обе задачи выполняют блокировку отправки одновременно, которая затем зависает. Он не зависает с меньшими наборами данных, поскольку библиотека MPI имеет достаточно большой буфер для хранения данных. Но как только вы увеличите его до размера буфера, send блокирует оба процесса. Так как ни один процесс не пытается получить, ни один буфер не может очиститься, и программа блокируется.
Чтобы исправить это, сначала попросите подчиненное устройство получить от ведущего, а затем отправить данные обратно. Если ваши отправка / получение не конфликтуют, вы можете изменить порядок функций. В противном случае вам нужно создать временный буфер для его хранения.
Заменить все в сочетании MPI_Send/MPI_Recv
звонки с звонками на MPI_Sendrecv
, Например, это
MPI_Send(&phi[e-px*py], px*py, MPI_DOUBLE, taskid+1, 1, MPI_COMM_WORLD);
MPI_Recv(&phi[e], px*py, MPI_DOUBLE, taskid+1, 1, MPI_COMM_WORLD, &status);
становится
MPI_Sendrecv(&phi[e-px*py], px*py, MPI_DOUBLE, taskid+1, 1,
&phi[e], px*px, MPI_DOUBLE, taskid+1, 1,
MPI_COMM_WORLD, &status);
MPI_Sendrecv
использует неблокирующие операции внутри себя и, таким образом, не блокируется, даже если два ранга отправляют друг другу одновременно. Единственное требование (как обычно) состоит в том, что каждому отправлению соответствует получение.