最慢的处理器MPI输出

3

我正在使用MPI编写程序。每个处理器都执行一个for循环:

int main(int argc, char** argv) {
  boost::mpi::environment env(argc, argv);

  for( int i=0; i<10; ++i ) {
    std::cout << "Index " << i << std::endl << std::flush;
  }
}

有没有办法使cout仅在最后一个处理器命中索引i时发生?或者标记,使一行只在最后一个处理器执行它时执行?


1
我不知道如何使其仅打印最后一个,但获得相同效果的另一种方法是在std::cout循环之后放置一个MPI_Barrier,并在屏障之后立即使用if(rank == 0) { std::cout << std::endl; }。只有在绝对必要时才这样做,因为不必要地引入同步块是确保代码不可扩展的好方法。 - R_Kapp
谢谢!是的,我也想到了这个解决方案,但我只是想打印出一些指示代码所在的位置...这对计算工作并不必要。每次迭代需要一段时间,我需要运行很多次,我喜欢知道模拟进行到了哪个阶段。 - ad_ad
我不知道是否有更优雅/高效的方法,但您可以在运行此代码之前将原子计数器初始化为0,并在每个线程完成迭代时递增计数器。然后,每个线程都可以查看计数器以查看它是否是最后一个,如果是,则输出cout。 - RyanP
1个回答

10
这可能看起来微不足道,但实际上,在像MPI这样的分布式内存模型中,您所要求的内容非常复杂...
在共享内存环境中,例如OpenMP,可以通过定义一个由所有线程原子地递增的共享计数器来轻松解决这个问题,并在之后检查它的值是否与线程数相对应。如果是这样,那么就意味着所有线程都通过了该点,并且当前线程是最后一个,他将负责打印。
在分布式环境中,定义和更新这样的共享变量非常复杂,因为每个进程都可能在远程机器上运行。为了仍然允许这样做,MPI自MPI-2.0以来提出了内存窗口和单边通信。然而,即使有了这些,也无法正确地实现原子计数器递增并可靠地获取其值。只有MPI 3.0引入了MPI_Fetch_and_op()函数,才能实现这一点。以下是一个实现示例:
#include <mpi.h>
#include <iostream>

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

    // initialisation and inquiring of rank and size
    MPI_Init( &argc, &argv);

    int rank, size;
    MPI_Comm_rank( MPI_COMM_WORLD, &rank );
    MPI_Comm_size( MPI_COMM_WORLD, &size );

    // creation of the "shared" counter on process of rank 0
    int *addr = 0, winSz = 0;
    if ( rank == 0 ) {
        winSz = sizeof( int );
        MPI_Alloc_mem( winSz, MPI_INFO_NULL, &addr );
        *addr = 1; // initialised to 1 since MPI_Fetch_and_op returns value *before* increment
    }
    MPI_Win win;
    MPI_Win_create( addr, winSz, sizeof( int ), MPI_INFO_NULL, MPI_COMM_WORLD, &win );

    // atomic incrementation of the counter
    int counter, one = 1;
    MPI_Win_lock( MPI_LOCK_EXCLUSIVE, 0, 0, win );
    MPI_Fetch_and_op( &one, &counter, MPI_INT, 0, 0, MPI_SUM, win );
    MPI_Win_unlock( 0, win );

    // checking the value of the counter and printing by last in time process
    if ( counter == size ) {
        std::cout << "Process #" << rank << " did the last update" << std::endl;
    }

    // cleaning up
    MPI_Win_free( &win );
    if ( rank == 0 ) {
        MPI_Free_mem( addr );
    }
    MPI_Finalize();

    return 0;
}

您可以看到,对于这样一个微不足道的请求来说,这是相当冗长和复杂的。而且,这需要MPI 3.0支持。

不幸的是,Boost.MPI似乎是您的目标,但只“支持MPI 1.1中的大部分功能”。因此,如果您真的想要获得这个功能,您将不得不使用一些纯MPI编程。


这个例子非常有价值,因为很难找到一个针对 MPI_Fetch_and_op 的简单示例。 - Shibli

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接