与多对多处理器通信时出现 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。 这解决了我的问题

谢谢大家的回答!