Multithreading 在MPI中使用线程(OpenMP)进行MPI\u b广播

Multithreading 在MPI中使用线程(OpenMP)进行MPI\u b广播,multithreading,mpi,openmp,Multithreading,Mpi,Openmp,MPI标准3.0在第5.13节中指出 最后,在多线程实现中,可以有多个, 在进程上并发执行的集体通信调用。在里面 在这些情况下,用户有责任确保 两个不同的集体不能同时使用同一个通讯器 在同一进程中进行通信调用 我编写了以下程序,该程序不能正确执行,但编译并转储了一个内核 void main(int argc, char *argv[]) { int required = MPI_THREAD_MULTIPLE, provided, rank, size, threadID, threadProc

MPI标准3.0在第5.13节中指出

最后,在多线程实现中,可以有多个, 在进程上并发执行的集体通信调用。在里面 在这些情况下,用户有责任确保 两个不同的集体不能同时使用同一个通讯器 在同一进程中进行通信调用

我编写了以下程序,该程序不能正确执行,但编译并转储了一个内核

void main(int argc, char *argv[])
{
int required = MPI_THREAD_MULTIPLE, provided, rank, size, threadID, threadProcRank ; 
MPI_Comm comm = MPI_COMM_WORLD ; 

MPI_Init_thread(&argc, &argv, required, &provided);
MPI_Comm_size(comm, &size);
MPI_Comm_rank(comm, &rank);

int buffer1[10000] = {0} ;
int buffer2[10000] = {0} ; 

#pragma omp parallel private(threadID,threadProcRank) shared(comm, buffer1)
{
    threadID = omp_get_thread_num();
    MPI_Comm_rank(comm, &threadProcRank);
    printf("\nMy thread ID is %d and I am in process ranked %d", threadID, threadProcRank);

    if(threadID == 0)
        MPI_Bcast(buffer1, 10000, MPI_INTEGER, 0, comm);

    If (threadID == 1)
        MPI_Bcast(buffer1, 10000, MPI_INTEGER, 0, comm);
}

    MPI_Finalize();
}

我的问题是:每个进程中有两个线程(线程ID为0,线程ID为1)发布一个广播调用,该调用可以作为根进程(即进程0)中的MPI_发送。我将其解释为MPI_发送的两个循环,其中目标是剩余的进程。目标进程还在线程ID 0和线程ID 1中发布MPI_Bcast。这些可以看作是两个线程中每个进程发布的两个MPI_Recv。由于MPI_Bcast是相同的,因此在接收由根进程0发送的消息时应该没有匹配问题。但该计划仍然不起作用。为什么?是不是因为信息可能会在同一个通讯器的不同/相同集合中混淆?既然MPI mpich2看到了这种可能性,它就不允许在同一个通信器上同时挂起两个集合

首先,如果MPI实现返回实际提供的线程支持级别,则不检查提供的值。该标准允许此级别低于请求的级别,正确的MPI应用程序更愿意执行以下操作:

MPI_Init_线程&argc和&argv,必需和提供; 如果提供<需要 { printfError:MPI不提供所需的线程支持\n; MPI_AbortMPI_COMM_WORLD,1; 出口1; } 其次,这一行代码是多余的:

MPI_Comm_rankcomm和THREADPROCLANK; MPI中的线程没有单独的列组-只有进程有列组。有人建议在MPI 3.0中引入所谓的端点,这将允许单个进程具有多个列组,并将它们绑定到不同的线程,但没有将其纳入标准的最终版本

第三,在两个集合中使用相同的缓冲区变量。我猜您的意图是在线程0的调用中使用buffer1,在线程1的调用中使用buffer2。此外,MPI_INTEGER是Fortran中与INTEGER相对应的数据类型。对于C int类型,相应的MPI数据类型为MPI_int

第四,将MPI_BCAST解释为一个MPI_SEND循环和相应的MPI_RECV只是一种解释。在现实中,实现是非常不同的-请参阅。例如,对于初始网络设置延迟远高于物理数据传输时间的较小消息,使用二叉树和二叉树以最小化集合的延迟。较大的消息通常被分成许多段,然后使用管道将这些段从根列组传递到所有其他列组。即使在树分布的情况下,消息仍然可以被分割

问题是,在实践中,每个集合操作都是使用具有相同标记的消息实现的,通常带有负标记值,应用程序程序员不允许使用这些值。这意味着在您的情况下,两个MPI_Bcast调用将使用相同的标记来传输其消息,并且由于列组相同且通信器相同,因此消息将全部混淆。因此,只在单独的传播者身上进行并行集体活动的要求

程序崩溃的原因有两个。原因之一是MPI库不提供MPI线程。第二个原因是,如果消息被分成两个大小不均的块,例如较大的第一部分和较小的第二部分。两个集合调用之间的干扰可能会导致第二个线程在等待第二个较小的块时接收指向第一个线程的较大第一块。结果将是消息截断,并调用中止MPI错误处理程序。这通常不会导致segfault和core转储,因此我假设您的MPICH2并没有被编译为线程安全的


这不是特定于MPICH2的。OpenMPI和其他实现也容易受到同样的限制。

首先,您没有检查提供的值,MPI实现返回实际提供的线程支持级别。该标准允许此级别低于请求的级别,正确的MPI应用程序更愿意执行以下操作:

MPI_Init_线程&argc和&argv,必需和提供; 如果提供<需要 { printfError:MPI不提供所需的线程支持\n; MPI_AbortMPI_COMM_WORLD,1; 出口1; } 其次,这一行代码是多余的:

MPI _Comm_rankcomm和threadProcRank; MPI中的线程没有单独的列组-只有进程有列组。有人建议在MPI 3.0中引入所谓的端点,这将允许单个进程具有多个列组,并将它们绑定到不同的线程,但没有将其纳入标准的最终版本

第三,在两个集合中使用相同的缓冲区变量。我猜您的意图是在线程0的调用中使用buffer1,在线程1的调用中使用buffer2。此外,MPI_INTEGER是Fortran中与INTEGER相对应的数据类型。对于C int类型,相应的MPI数据类型为MPI_int

第四,将MPI_BCAST解释为一个MPI_SEND循环和相应的MPI_RECV只是一种解释。在现实中,实现是非常不同的-请参阅。例如,对于初始网络设置延迟远高于物理数据传输时间的较小消息,使用二叉树和二叉树以最小化集合的延迟。较大的消息通常被分成许多段,然后使用管道将这些段从根列组传递到所有其他列组。即使在树分布的情况下,消息仍然可以被分割

问题是,在实践中,每个集合操作都是使用具有相同标记的消息实现的,通常带有负标记值,应用程序程序员不允许使用这些值。这意味着在您的情况下,两个MPI_Bcast调用将使用相同的标记来传输其消息,并且由于列组相同且通信器相同,因此消息将全部混淆。因此,只在单独的传播者身上进行并行集体活动的要求

程序崩溃的原因有两个。原因之一是MPI库不提供MPI线程。第二个原因是,如果消息被分成两个大小不均的块,例如较大的第一部分和较小的第二部分。两个集合调用之间的干扰可能会导致第二个线程在等待第二个较小的块时接收指向第一个线程的较大第一块。结果将是消息截断,并调用中止MPI错误处理程序。这通常不会导致segfault和core转储,因此我假设您的MPICH2并没有被编译为线程安全的


这不是特定于MPICH2的。Open MPI和其他实现也容易受到同样的限制。

感谢您抽出时间详细回答。首先:我检查了提供的值,它是MPI\u线程\u的倍数。第二:我故意这样做是为了看看哪个线程位于哪个进程中第三:再次故意这样做,因为我希望MPI_Bcast看起来相同。第四:关于二叉树和二叉树的实现,您是对的,但是我希望只有两个进程可以直接进行简单的发送和接收。关于碎片,你提出了一个非常好的观点——我已经将它添加到我的想法中:第五:某个地方MPI_Bcast正在使用标记,因为我可以捕获一个带有MPI_任何标记的MPI_Recv的MPI_Bcast。非常感谢!感谢您抽出时间详细回答。首先:我检查了提供的值,它是MPI\u THREAD\u的倍数。第二:我故意这样做是为了看看哪个线程位于哪个进程中第三:再次故意这样做,因为我希望MPI_Bcast看起来相同。第四:关于二叉树和二叉树的实现,您是对的,但是我希望只有两个进程可以直接进行简单的发送和接收。关于碎片,你提出了一个非常好的观点——我已经将它添加到我的想法中:第五:某个地方MPI_Bcast正在使用标记,因为我可以捕获一个带有MPI_任何标记的MPI_Recv的MPI_Bcast。非常感谢!