如何使用 C++ 仅为 Open/Microsoft MPI 中的某些进程创建临界区?

How to create a critical section only for some processes in Open/Microsoft MPI using C++?

我是 MPI 的新手,想知道如何为 运行 进程中的少数进程创建临界区。我知道我可以为所有进程创建一个这样的进程:

for (int i = 0; i < 1000; ++i)
{
    MPI_Barrier(MPI_COMM_WORLD);
    std::cout << "RANK " << rank << " PRINTS " << i << std::endl;
}

然而,如果至少有一个进程不会触发 MPI_Barrier(),这将不起作用。假设我这样做了:

if(rank > 0)
for (int i = 0; i < 1000; ++i)
{
    MPI_Barrier(MPI_COMM_WORLD);
    std::cout << "RANK " << rank << " PRINTS " << i << std::endl;
}

这会崩溃,因为第 0 个进程会跳过循环。那么我如何让所有其他进程同步打印并对第 0 个进程执行其他操作(假设所有打印结束时等待消息)?

正如 @Gilles Gouaillardet 指出的那样,您可以使用 MPI_Comm_split 创建一个没有等级 0 的通信器。 MPI_Barrier 可以用这个新的通信器调用同步其余进程。 Rank 0,完成它的操作后可以在 COMM_WORLD 上调用 barrier 并等待其余进程调用它。

其余进程,比如 1 到 N,可以通过使用 for loop 迭代 1N 来顺序执行代码区域(顺便说一句,不是 MPI 方法)和一个 if 块,它基于 rankMPI_Barrier 执行代码区域,如下例所示。

MPI_Comm_split(MPI_COMM_WORLD,color,key,newcomm) // create new comm without rank 0
if(rank>0) {
  for (int i = 1; i < size; ++i)
   {
    if(rank == i) { // Critical region. Only one rank can enter here at a time. Here it enters sequentially from 0 to size
       std::cout << "RANK " << rank << " PRINTS " << i << std::endl; // After calling Critical Session Part, call barrier to signal other processes.
       MPI_Barrier(newcomm);
    } else {
       MPI_Barrier(newcomm); // all other proesses (except rank == i) should wait and do not enter the critical region
    }
   }
       MPI_Barrier(MPI_COMM_WORLD);// This barrier will be called with rank 0         
} else {
       //do some work for rank 0
       // wait for the rest of the processes
       MPI_Barrier(MPI_COMM_WORLD);         
}

另一种方法(无需创建新的通信器)是您可以使用环形发送消息(环形广播)的概念。

if (rank == 0) {
    value = 1; 
    MPI_Send( &value, 1, MPI_INT, rank + 1, 0, MPI_COMM_WORLD );
    // Do something useful
}
else {
    MPI_Recv( &value, 1, MPI_INT, rank - 1, 0, MPI_COMM_WORLD, 
          &status );
    if (rank < size - 1){ 
    //criticalregion
    MPI_Send( &value, 1, MPI_INT, rank + 1, 0, MPI_COMM_WORLD );
    }
}
MPI_Barrier(MPI_COMM_WORLD);         

在这里,rank 0 将消息发送给 rank 1,后者又发送给 rank 2,依此类推。这样,进程收到消息后,可以顺序执行代码(临界区),然后将消息发送到更高级别并触发执行。