2015-08-31 50 views
6

经过多次谷歌搜索,我不知道是什么原因造成这个问题。在这里它是:在MPI_Allgather奇怪的死锁

我有一个简单的调用MPI_Allgather在我的代码,我有双重,三重和四重检查是正确的(发送/接收缓冲区大小正确;呼叫中的发送/接收大小是正确),但对于“大量”进程,我会遇到死锁或MPI_ERR_TRUNCATE。用于Allgather的通信器使用MPI_Comm_split从MPI_COMM_WORLD中分离出来。对于我目前的测试,等级0进入一个传播者,其余的等级进入第二传播者。对于总共6个或更少的队伍,Allgather工作得很好。如果我使用7个等级,我会得到一个MPI_ERR_TRUNCATE。 8个等级,僵局。我已经验证了传播者被正确地分割了(MPI_Comm_rank和MPI_Comm_size在两个Comms的所有等级上都是正确的)。

我已经手动验证了每个发送和接收缓冲区的大小以及接收的最大数量。我的第一个解决方法是将MPI_Allgather交换为MPI_Gather的for-loop到每个进程。这适用于这种情况,但改变给我的代码的网格(使用METIS分割CFD网格)带来了问题。现在我的解决方案,我还没有能够打破,是用Allgatherv取代,我认为它更有效率,因为我有不同数量的数据被发送每个过程。

下面是(我希望)在上下文中相关的违规代码;如果我错过了某些东西,那么Allgather在this file的第599行。

// Get the number of mpiFaces on each processor (for later communication) 
    // 'nProgGrid' is the size of the communicator 'gridComm' 
    vector<int> nMpiFaces_proc(nProcGrid); 

    // This MPI_Allgather works just fine, every time  
    // int nMpiFaces is assigned on preceding lines 
    MPI_Allgather(&nMpiFaces,1,MPI_INT,nMpiFaces_proc.data(),1,MPI_INT,gridComm); 

    int maxNodesPerFace = (nDims==2) ? 2 : 4; 
    int maxNMpiFaces = getMax(nMpiFaces_proc); 
    // The matrix class is just a fancy wrapper around std::vector that 
    // allows for (i,j) indexing. The getSize() and getData() methods just 
    // call the size() and data() methods, respectively, of the underlying 
    // vector<int> object. 
    matrix<int> mpiFaceNodes_proc(nProcGrid,maxNMpiFaces*maxNodesPerFace); 
    // This is the MPI_Allgather which (sometimes) doesn't work. 
    // vector<int> mpiFaceNodes is assigned in preceding lines 
    MPI_Allgather(mpiFaceNodes.data(),mpiFaceNodes.size(),MPI_INT, 
       mpiFaceNodes_proc.getData(),maxNMpiFaces*maxNodesPerFace, 
       MPI_INT,gridComm); 

我目前使用的openmpi 1.6.4,克++ 4.9.2和AMD FX-8350 8芯用的RAM 16GB处理器,运行基本OS弗雷亚0.3的最新的更新(基本上Ubuntu的14.04) 。但是,在另一台使用CentOS,Intel硬件和MPICH2的计算机上,我也遇到了这个问题。

任何想法?我听说可以更改MPI的内部缓冲区大小来解决类似的问题,但快速尝试这样做(如http://www.caps.ou.edu/pipermail/arpssupport/2002-May/000361.html所示)不起作用。

作为参考,这个问题是非常类似于这里显示的:https://software.intel.com/en-us/forums/topic/285074,除了在我的情况下,我只有一个处理器8核心,在一台台式计算机上。

UPDATE 我已经成功地拼凑这个失败的简约例如:在

mpicxx -std=c++11 mpiTest.cpp -o mpitest 
mpirun -np 8 ./mpitest 

与下面的输出:

#include <iostream> 
#include <vector> 
#include <stdlib.h> 
#include <time.h> 

#include "mpi.h" 

using namespace std; 

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

    int rank, nproc, newID, newRank, newSize; 
    MPI_Comm newComm; 
    MPI_Comm_rank(MPI_COMM_WORLD,&rank); 
    MPI_Comm_size(MPI_COMM_WORLD,&nproc); 

    newID = rank%2; 
    MPI_Comm_split(MPI_COMM_WORLD,newID,rank,&newComm); 
    MPI_Comm_rank(newComm,&newRank); 
    MPI_Comm_size(newComm,&newSize); 

    srand(time(NULL)); 

    // Get a different 'random' number for each rank on newComm 
    //int nSend = rand()%10000; 
    //for (int i=0; i<newRank; i++) nSend = rand()%10000; 

    /*! -- Found a set of #'s which fail for nproc=8: -- */ 
    int badSizes[4] = {2695,7045,4256,8745}; 
    int nSend = badSizes[newRank]; 

    cout << "Comm " << newID << ", rank " << newRank << ": nSend = " << nSend << endl; 

    vector<int> send(nSend); 
    for (int i=0; i<nSend; i++) 
    send[i] = rand(); 

    vector<int> nRecv(newSize); 
    MPI_Allgather(&nSend,1,MPI_INT,nRecv.data(),1,MPI_INT,newComm); 

    int maxNRecv = 0; 
    for (int i=0; i<newSize; i++) 
    maxNRecv = max(maxNRecv,nRecv[i]); 

    vector<int> recv(newSize*maxNRecv); 
    MPI_Barrier(MPI_COMM_WORLD); 
    cout << "rank " << rank << ": Allgather-ing data for communicator " << newID << endl; 
    MPI_Allgather(send.data(),nSend,MPI_INT,recv.data(),maxNRecv,MPI_INT,newComm); 
    cout << "rank " << rank << ": Done Allgathering-data for communicator " << newID << endl; 

    MPI_Finalize(); 
    return 0; 
} 

上面的代码被编译和运行我的16核心CentOS和我的8核心Ubuntu机器:

Comm 0, rank 0: nSend = 2695 
Comm 1, rank 0: nSend = 2695 
Comm 0, rank 1: nSend = 7045 
Comm 1, rank 1: nSend = 7045 
Comm 0, rank 2: nSend = 4256 
Comm 1, rank 2: nSend = 4256 
Comm 0, rank 3: nSend = 8745 
Comm 1, rank 3: nSend = 8745 
rank 5: Allgather-ing data for communicator 1 
rank 6: Allgather-ing data for communicator 0 
rank 7: Allgather-ing data for communicator 1 
rank 0: Allgather-ing data for communicator 0 
rank 1: Allgather-ing data for communicator 1 
rank 2: Allgather-ing data for communicator 0 
rank 3: Allgather-ing data for communicator 1 
rank 4: Allgather-ing data for communicator 0 
rank 5: Done Allgathering-data for communicator 1 
rank 3: Done Allgathering-data for communicator 1 
rank 4: Done Allgathering-data for communicator 0 
rank 2: Done Allgathering-data for communicator 0 

请注意,每个传播者只有2个队伍退出Allgather;这不是在我的实际代码中发生的事情('破坏'的通信器上没有队列退出Allgather),但最终结果是一样的 - 代码挂起,直到我杀了它。

我猜这与每个进程发送数量不同有关,但据我所见,从MPI文档和教程中可以看出,这应该是允许的,正确的?当然,MPI_Allgatherv稍微适用一些,但为了简单起见,我一直在使用Allgather。

+2

您的第二个代码段与您的描述不一致。如何发布您正在运行的实际代码?并尝试创建MCVE。 – Jeff

+0

我已经更新了代码片段并提供了一个完整(2000+行)文件的链接;我原来的MPI_Gather解决方法停止工作,所以我删除了该片段。我可能有一些运气在一个简单的程序中复制了效果,该程序在每个等级上分配一个随机大小的矢量(最多可达10,000个int)并执行上面的MPI_Allgather,但我不确定,我可能不会今天能够回到它。当我有机会时,我会发布它。 – Jacob

+2

如果您在diff procs上使用rand,则参数将不匹配。这是对MPI的无效使用。在root和bcast上调用rand以保持一致。 – Jeff

回答

4

如果输入计数在所有进程中不相同,则必须使用MPI_Allgatherv

确切地说,必须匹配的是类型签名count,type,因为从技术上讲,您可以使用不同的数据类型获得相同的基本表示形式(例如N个元素与1个连续类型的N个元素),但是如果您在所有地方都使用相同的论点,这是MPI集体的常见用法,那么你的数量必须与所有地方匹配。

最新MPI标准的相关部分(3.1)为165页上:

与sendcount个,sendtype相关联,在过程 必须等于与recvcount相关联的类型的签名的类型签名, recvtype在任何其他进程。