与多对多处理器通信时出现 MPI 错误
MPI error in communicating with many to many processors
我正在编写代码,其中每个处理器必须与多个处理器交互。
例如:我有 12 个处理器,因此处理器 0 必须进行通信以表示 1、2、10 和 9。让我们称它们为处理器 0 的邻居。同样,我有
处理器 1 必须与 5 ,3 通信。
处理器 2 必须与 5、1、0、10、11 通信
等等。
数据流有两种方式,即处理器 0 必须向 1、2、10 和 9 发送数据,并从它们接收数据。
此外,Tag 计算也没有问题。
我创建了一个这样工作的代码:
for(all neighbours)
{
store data in vector<double> x;
MPI_Send(x)
}
MPI_BARRIER();
for(all neighbours)
{
MPI_Recv(x);
do work with x
}
现在我针对不同大小的 x 和不同的邻居排列测试此算法。该代码对某些人有效,但对其他人无效,它只是求助于死锁。
我也试过:
for(all neighbours)
{
store data in vector<double> x;
MPI_ISend(x)
}
MPI_Test();
for(all neighbours)
{
MPI_Recv(x);
do work with x
}
结果是一样的,虽然死锁在结果中被 NaN 取代,因为 MPI_Test() 告诉我一些 MPI_Isend() 操作没有完成并且它立即跳转到MPI_Recv().
任何人都可以在这件事上指导我,我错在哪里?还是我的基本方法本身不正确?
编辑:为了更好地理解问题,我附上了代码片段。我主要致力于并行化非结构化 3D-CFD 求解器
我已附上其中一份文件,并附有一些解释。我不是在广播,而是在父处理器的邻居上循环以通过接口发送数据(这可以定义为两个接口之间的边界)。
所以,如果说我有 12 个处理器,并且处理器 0 必须与 1、2、10 和 9 通信。那么 0 是父处理器,1、2、10 和 9 是它的邻居。
由于文件太长,而且是求解器的一部分,为简单起见,我只保留了MPI函数。
void Reader::MPI_InitializeInterface_Values() {
double nbr_interface_id;
Interface *interface;
MPI_Status status;
MPI_Request send_request, recv_request;
int err, flag;
int err2;
char buffer[MPI_MAX_ERROR_STRING];
int len;
int count;
for (int zone_no = 0; zone_no<this->GetNumberOfZones(); zone_no++) { // Number of zone per processor is 1, so basically each zone is an independent processor
UnstructuredGrid *zone = this->ZoneList[zone_no];
int no_of_interface = zone->GetNumberOfInterfaces();
// int count;
long int count_send = 0;
long int count_recv = 0;
long int max_size = 10000; // can be set from test case later
int max_size2 = 199;
int proc_no = FlowSolution::processor_number;
for (int interface_no = 0; interface_no < no_of_interface; interface_no++) { // interface is defined as a boundary between two zones
interface = zone->GetInterface(interface_no);
int no_faces = interface->GetNumberOfFaces();
if (no_faces != 0) {
std::vector< double > Variable_send; // The vector which stores the data to be sent across the interface
std::vector< double > Variable_recieve;
int total_size = FlowSolution::VariableOrder.size() * no_faces;
Variable_send.resize(total_size);
Variable_recieve.resize(total_size);
int nbr_proc_no = zone->GetInterface(interface_no)->GetNeighborZoneId(); // neighbour of parent processor
int j = 0;
nbr_interface_id = interface->GetShared_Interface_ID();
for (std::map<VARIABLE, int>::iterator iterator = FlowSolution::VariableOrder.begin(); iterator != FlowSolution::VariableOrder.end(); iterator++) {
for (int face_no = 0; face_no < no_faces; face_no++) {
Face *face = interface->GetFace(face_no);
int owner_id = face->Getinterface_Original_face_owner_id();
double value_send = zone->GetInterface(interface_no)->GetFace(face_no)->GetCell(owner_id)->GetPresentFlowSolution()->GetVariableValue((*iterator).first);
Variable_send[j] = value_send;
j++;
}
}
count_send = nbr_proc_no * max_size + nbr_interface_id; // tag for data to be sent
err2 = MPI_Isend(&Variable_send.front(), total_size, MPI_DOUBLE, nbr_proc_no, count_send, MPI_COMM_WORLD, &send_request);
}// end of sending
} // all the processors have sent data to their corresponding neighbours
MPI_Barrier(MPI_COMM_WORLD);
for (int interface_no = 0; interface_no < no_of_interface; interface_no++) { // loop over of neighbours of the current processor to receive data
interface = zone->GetInterface(interface_no);
int no_faces = interface->GetNumberOfFaces();
if (no_faces != 0) {
std::vector< double > Variable_recieve; // The vector which collects the data sent across the interface from
int total_size = FlowSolution::VariableOrder.size() * no_faces;
Variable_recieve.resize(total_size);
count_recv = proc_no * max_size + interface_no; // tag to receive data
int nbr_proc_no = zone->GetInterface(interface_no)->GetNeighborZoneId();
nbr_interface_id = interface->GetShared_Interface_ID();
MPI_Irecv(&Variable_recieve.front(), total_size, MPI_DOUBLE, nbr_proc_no, count_recv, MPI_COMM_WORLD, &recv_request);
/* Now some work is done using received data */
int j = 0;
for (std::map<VARIABLE, int>::iterator iterator = FlowSolution::VariableOrder.begin(); iterator != FlowSolution::VariableOrder.end(); iterator++) {
for (int face_no = 0; face_no < no_faces; face_no++) {
double value_recieve = Variable_recieve[j];
j++;
Face *face = interface->GetFace(face_no);
int owner_id = face->Getinterface_Original_face_owner_id();
interface->GetFictitiousCell(face_no)->GetPresentFlowSolution()->SetVariableValue((*iterator).first, value_recieve);
double value1 = face->GetCell(owner_id)->GetPresentFlowSolution()->GetVariableValue((*iterator).first);
double face_value = 0.5 * (value1 + value_recieve);
interface->GetFace(face_no)->GetPresentFlowSolution()->SetVariableValue((*iterator).first, face_value);
}
}
// Variable_recieve.clear();
}
}// end of receiving
}
从问题陈述开始:
- 处理器 0 必须向 1、2、9 和 10 发送数据,并从它们接收数据。
- 处理器 1 必须发送给 5 和 3,并从它们接收。
- 处理器 2 必须向 0、1、5、10 和 11 发送数据,并从它们接收数据。
- 总共有 12 个处理器。
如果您只是 运行 一个 12 步计划,您可以让生活更轻松:
- 第 1 步:处理器 0 发送,其他处理器根据需要接收,然后相反。
- 第 2 步:处理器 1 发送,其他处理器根据需要接收,然后相反。
- ...
- 第 12 步:获利 - 无事可做(因为所有其他处理器都已与处理器 11 交互)。
每个步骤都可以实现为 MPI_Scatterv(一些发送计数将为零),然后是 MPI_Gatherv。总共调用 22 次,您就完成了。
死锁可能有多种可能的原因,因此您必须更加具体,例如。 G。标准说:"When standard send operations are used, then a deadlock situation may occur where both processes are blocked because buffer space is not available."
您应该同时使用 Isend 和 Irecv。一般结构应该是:
MPI_Request req[n];
MPI_Irecv(..., req[0]);
// ...
MPI_Irecv(..., req[n-1]);
MPI_Isend(..., req[0]);
// ...
MPI_Isend(..., req[n-1]);
MPI_Waitall(n, req, MPI_STATUSES_IGNORE);
通过使用AllGatherV,问题可以得到解决。我所做的只是进行发送计数,以便发送计数只有我想与之通信的处理器。其他处理器的发送计数为 0。
这解决了我的问题
谢谢大家的回答!
我正在编写代码,其中每个处理器必须与多个处理器交互。
例如:我有 12 个处理器,因此处理器 0 必须进行通信以表示 1、2、10 和 9。让我们称它们为处理器 0 的邻居。同样,我有 处理器 1 必须与 5 ,3 通信。 处理器 2 必须与 5、1、0、10、11 通信 等等。 数据流有两种方式,即处理器 0 必须向 1、2、10 和 9 发送数据,并从它们接收数据。 此外,Tag 计算也没有问题。 我创建了一个这样工作的代码:
for(all neighbours)
{
store data in vector<double> x;
MPI_Send(x)
}
MPI_BARRIER();
for(all neighbours)
{
MPI_Recv(x);
do work with x
}
现在我针对不同大小的 x 和不同的邻居排列测试此算法。该代码对某些人有效,但对其他人无效,它只是求助于死锁。 我也试过:
for(all neighbours)
{
store data in vector<double> x;
MPI_ISend(x)
}
MPI_Test();
for(all neighbours)
{
MPI_Recv(x);
do work with x
}
结果是一样的,虽然死锁在结果中被 NaN 取代,因为 MPI_Test() 告诉我一些 MPI_Isend() 操作没有完成并且它立即跳转到MPI_Recv().
任何人都可以在这件事上指导我,我错在哪里?还是我的基本方法本身不正确?
编辑:为了更好地理解问题,我附上了代码片段。我主要致力于并行化非结构化 3D-CFD 求解器
我已附上其中一份文件,并附有一些解释。我不是在广播,而是在父处理器的邻居上循环以通过接口发送数据(这可以定义为两个接口之间的边界)。
所以,如果说我有 12 个处理器,并且处理器 0 必须与 1、2、10 和 9 通信。那么 0 是父处理器,1、2、10 和 9 是它的邻居。
由于文件太长,而且是求解器的一部分,为简单起见,我只保留了MPI函数。
void Reader::MPI_InitializeInterface_Values() {
double nbr_interface_id;
Interface *interface;
MPI_Status status;
MPI_Request send_request, recv_request;
int err, flag;
int err2;
char buffer[MPI_MAX_ERROR_STRING];
int len;
int count;
for (int zone_no = 0; zone_no<this->GetNumberOfZones(); zone_no++) { // Number of zone per processor is 1, so basically each zone is an independent processor
UnstructuredGrid *zone = this->ZoneList[zone_no];
int no_of_interface = zone->GetNumberOfInterfaces();
// int count;
long int count_send = 0;
long int count_recv = 0;
long int max_size = 10000; // can be set from test case later
int max_size2 = 199;
int proc_no = FlowSolution::processor_number;
for (int interface_no = 0; interface_no < no_of_interface; interface_no++) { // interface is defined as a boundary between two zones
interface = zone->GetInterface(interface_no);
int no_faces = interface->GetNumberOfFaces();
if (no_faces != 0) {
std::vector< double > Variable_send; // The vector which stores the data to be sent across the interface
std::vector< double > Variable_recieve;
int total_size = FlowSolution::VariableOrder.size() * no_faces;
Variable_send.resize(total_size);
Variable_recieve.resize(total_size);
int nbr_proc_no = zone->GetInterface(interface_no)->GetNeighborZoneId(); // neighbour of parent processor
int j = 0;
nbr_interface_id = interface->GetShared_Interface_ID();
for (std::map<VARIABLE, int>::iterator iterator = FlowSolution::VariableOrder.begin(); iterator != FlowSolution::VariableOrder.end(); iterator++) {
for (int face_no = 0; face_no < no_faces; face_no++) {
Face *face = interface->GetFace(face_no);
int owner_id = face->Getinterface_Original_face_owner_id();
double value_send = zone->GetInterface(interface_no)->GetFace(face_no)->GetCell(owner_id)->GetPresentFlowSolution()->GetVariableValue((*iterator).first);
Variable_send[j] = value_send;
j++;
}
}
count_send = nbr_proc_no * max_size + nbr_interface_id; // tag for data to be sent
err2 = MPI_Isend(&Variable_send.front(), total_size, MPI_DOUBLE, nbr_proc_no, count_send, MPI_COMM_WORLD, &send_request);
}// end of sending
} // all the processors have sent data to their corresponding neighbours
MPI_Barrier(MPI_COMM_WORLD);
for (int interface_no = 0; interface_no < no_of_interface; interface_no++) { // loop over of neighbours of the current processor to receive data
interface = zone->GetInterface(interface_no);
int no_faces = interface->GetNumberOfFaces();
if (no_faces != 0) {
std::vector< double > Variable_recieve; // The vector which collects the data sent across the interface from
int total_size = FlowSolution::VariableOrder.size() * no_faces;
Variable_recieve.resize(total_size);
count_recv = proc_no * max_size + interface_no; // tag to receive data
int nbr_proc_no = zone->GetInterface(interface_no)->GetNeighborZoneId();
nbr_interface_id = interface->GetShared_Interface_ID();
MPI_Irecv(&Variable_recieve.front(), total_size, MPI_DOUBLE, nbr_proc_no, count_recv, MPI_COMM_WORLD, &recv_request);
/* Now some work is done using received data */
int j = 0;
for (std::map<VARIABLE, int>::iterator iterator = FlowSolution::VariableOrder.begin(); iterator != FlowSolution::VariableOrder.end(); iterator++) {
for (int face_no = 0; face_no < no_faces; face_no++) {
double value_recieve = Variable_recieve[j];
j++;
Face *face = interface->GetFace(face_no);
int owner_id = face->Getinterface_Original_face_owner_id();
interface->GetFictitiousCell(face_no)->GetPresentFlowSolution()->SetVariableValue((*iterator).first, value_recieve);
double value1 = face->GetCell(owner_id)->GetPresentFlowSolution()->GetVariableValue((*iterator).first);
double face_value = 0.5 * (value1 + value_recieve);
interface->GetFace(face_no)->GetPresentFlowSolution()->SetVariableValue((*iterator).first, face_value);
}
}
// Variable_recieve.clear();
}
}// end of receiving
}
从问题陈述开始:
- 处理器 0 必须向 1、2、9 和 10 发送数据,并从它们接收数据。
- 处理器 1 必须发送给 5 和 3,并从它们接收。
- 处理器 2 必须向 0、1、5、10 和 11 发送数据,并从它们接收数据。
- 总共有 12 个处理器。
如果您只是 运行 一个 12 步计划,您可以让生活更轻松:
- 第 1 步:处理器 0 发送,其他处理器根据需要接收,然后相反。
- 第 2 步:处理器 1 发送,其他处理器根据需要接收,然后相反。
- ...
- 第 12 步:获利 - 无事可做(因为所有其他处理器都已与处理器 11 交互)。
每个步骤都可以实现为 MPI_Scatterv(一些发送计数将为零),然后是 MPI_Gatherv。总共调用 22 次,您就完成了。
死锁可能有多种可能的原因,因此您必须更加具体,例如。 G。标准说:"When standard send operations are used, then a deadlock situation may occur where both processes are blocked because buffer space is not available."
您应该同时使用 Isend 和 Irecv。一般结构应该是:
MPI_Request req[n];
MPI_Irecv(..., req[0]);
// ...
MPI_Irecv(..., req[n-1]);
MPI_Isend(..., req[0]);
// ...
MPI_Isend(..., req[n-1]);
MPI_Waitall(n, req, MPI_STATUSES_IGNORE);
通过使用AllGatherV,问题可以得到解决。我所做的只是进行发送计数,以便发送计数只有我想与之通信的处理器。其他处理器的发送计数为 0。 这解决了我的问题
谢谢大家的回答!