我已经使用MPI_Gather和MPI_Bcast在我的模拟代码中实现了并行运行功能。我的MPI知识非常有限,但到目前为止,我已经成功运行了我的代码。最近,我开始实现一项新功能。首先,我在串口中实现并测试了这个功能,并且它可以工作。然后为了实现并行功能,我使用了MPI_Gather,就像我过去一样。这没有用,导致了这个问题。
让我解释一下我如何使用MPI。我的模拟器由两个模拟器组成(模拟两个耦合过程):一个管理子模拟器的主模拟器和两个交换信息。主人负责创建和管理MPI流程。根进程负责子模拟。我为主服务器使用Open MPI,为子服务器使用OpenMP(根进程生成多个线程)。运行子模拟的根进程需要来自主模拟器的MPI子进程的信息。对于任何给定的数组,我使用MPI_Gather将此信息从所有子进程传输到根目录。正如我所说的,我已经成功地将它用于三个阵列。
当我对第四个数组尝试相同的事情时,我看到MPI_Gather按预期工作了几个时间步,然后我在接收缓冲区中获取与root以外的子进程相对应的位置的垃圾值。由于数组包含在代码的其他部分中求解线性系统的结果,因此我尝试使用在调用MPI_Gather之前创建的非常简单的数组来简化问题。我能够在我的模拟器中使用以下代码重现相同的错误:
MPI_Comm comm;
comm = PETSC_COMM_WORLD;
int nTotProcs;
MPI_Comm_size(comm, &nTotProcs);
int rank=0; MPI_Comm_rank(comm, &rank);
// Task: Create an array for each subproc, gather this array in the root
int len=10; double * rbuf;
double a1[len];
// populate the array
for(int i=0; i<len; i++) {
a1[i]=rank+i+rank*0.1;
std::cout<<"P"<<rank<<" "<<a1[i]<<"\n";
}
if(rank==0) rbuf = new double[nTotProcs*len];
int error_code=MPI_Gather(a1,len,MPI_DOUBLE,rbuf,len,MPI_DOUBLE,0,comm);
std::cout<<"errorcode "<<error_code<<"\n";
// MPI error handling
MPI_Errhandler_set(comm, MPI_ERRORS_RETURN);
if (error_code != MPI_SUCCESS) {
char error_string[500];
int length_of_error_string;
MPI_Error_string(error_code, error_string, &length_of_error_string);
std::cout<<rank<<": "<<error_string<<"\n";
}
if(rank==0) {
for(int i=0;i<nTotProcs*len;i++)
std::cout<<"rbuf "<<rbuf[i]<<"\n";
delete[] rbuf;
}
具有两个MPI进程的输出(用于为子模拟生成24个线程的根)是:
P0 0
P0 1
P0 2
P0 3
P0 4
P0 5
P0 6
P0 7
P0 8
P0 9
errorcode 0
rbuf 0
rbuf 1
rbuf 2
rbuf 3
rbuf 4
rbuf 5
rbuf 6
rbuf 7
rbuf 8
rbuf 9
rbuf 1.1
rbuf 2.1
rbuf 3.1
rbuf 4.1
rbuf 5.1
rbuf 6.1
rbuf 7.1
rbuf 8.1
rbuf 9.1
rbuf 10.1
P1 1.1
P1 2.1
P1 3.1
P1 4.1
P1 5.1
P1 6.1
P1 7.1
P1 8.1
P1 9.1
P1 10.1
errorcode 0
P0 0
P0 1
P0 2
P0 3
P0 4
P0 5
P0 6
P0 7
P0 8
P0 9
errorcode 1
0: MPI_ERR_BUFFER: invalid buffer pointer
rbuf 0
rbuf 1
rbuf 2
rbuf 3
rbuf 4
rbuf 5
rbuf 6
rbuf 7
rbuf 8
rbuf 9
rbuf 0.0383135
rbuf 0.0370555
rbuf 0.0375653
rbuf 0.0378906
rbuf 0.0380734
rbuf 0.0381425
rbuf 0.0380751
rbuf 0.0378907
rbuf 0.0375654
rbuf 0.0370555
P1 1.1
P1 2.1
P1 3.1
P1 4.1
P1 5.1
P1 6.1
P1 7.1
P1 8.1
P1 9.1
P1 10.1
errorcode 0
P0 0
P0 1
P0 2
P0 3
P0 4
P0 5
P0 6
P0 7
P0 8
P0 9
errorcode 1
0: MPI_ERR_BUFFER: invalid buffer pointer
rbuf 0
rbuf 1
rbuf 2
rbuf 3
rbuf 4
rbuf 5
rbuf 6
rbuf 7
rbuf 8
rbuf 9
rbuf 0.01
rbuf 0.01
rbuf 0.01
rbuf 0.01
rbuf 0.01
rbuf 0.01
rbuf 0.01
rbuf 0.01
rbuf 0.01
rbuf 0.01
问题在rbuf输出的第二轮和第三轮中很明显。它不是1.1,2.1,而是一些随机值。实际上,我很确定这些随机值来自我的代码中的另一个数组,因为代码中有数组具有这些值。在代码崩溃之前,这些随机值会不断变化并从不同的数组中获取值。对应于根的前10个值始终是正确的,0到9.
我花了4-5天尝试调试此问题。代码中还有其他MPI_Gather提交可以正常工作。我不明白为什么这不起作用。哪个缓冲区指针无效?我使用gdb调试,我看到rbuf和a1是根P0中的有效指针,a1在子程序P1中有效。
上面的代码不是MWE,因为整个模拟器有其他部分,例如,一个组装线性系统Ax = b并使用牛顿方案解决它的部分。计算域在进程之间划分,每个子进程负责域中的网格元素集。所以我假设每个子程序花费不同的时间来达到某一行代码。在调用MPI_Gather之前是否必须调用MPI_Barrier(或类似的安排)?在过去,我没有那么做。即使在收集上述代码之前和之后放置屏障,我也会得到同样的错误。