在混合MPI / OpenMP中进行MPI调用的线程

时间:2015-11-07 18:55:30

标签: multithreading fortran mpi openmp hybrid

我在我的混合MPI / OpenMP代码中发现了一个问题 在下面引用的代码中以最简单的形式。我正在使用2个线程 每MPI排名。然后在OpenMP“Section”中使用这两个线程 进行多次计算,其中一项是对两个不同的向量A和B进行“mpi_allreduce”调用,其结果如下: 存储在W和WW中。问题是每次我运行程序 我最终得到了不同的输出。我的想法是MPI呼叫是 重叠和减少的阵列W和WW即使在它们时也被组合 有不同的名字,但我不确定。任何关于如何克服的评论 这个问题值得欢迎。

详细说明: MPI线程级别在代码中初始化为MPI_THREAD_MULTIPLE 但我也试过串行和漏斗(同样的问题)。

我编译代码mpiifort -openmp allreduce_omp_mpi.f90并为 我正在使用:

导出OMP_NUM_THREADS = 2 mpirun -np 3 ./a.out

      PROGRAM HELLO
      use mpi 
      use omp_lib
      IMPLICIT NONE

      INTEGER nthreads, tid 

      Integer Provided,mpi_err,myid,nproc
      CHARACTER(MPI_MAX_PROCESSOR_NAME):: hostname
      INTEGER :: nhostchars

      integer :: i
      real*8 :: A(1000), B(1000), W(1000),WW(1000)

      provided=0
      !Initialize MPI context
      call mpi_init_thread(MPI_THREAD_MULTIPLE,provided,mpi_err)
      CALL mpi_comm_rank(mpi_comm_world,myid,mpi_err)
      CALL mpi_comm_size(mpi_comm_world,nproc,mpi_err)
      CALL mpi_get_processor_name(hostname,nhostchars,mpi_err)

      !Initialize arrays
      A=1.0
      B=2.0
      !Check if MPI_THREAD_MULTIPLE is available
      if (provided >= MPI_THREAD_MULTIPLE) then
      write(6,*) ' mpi_thread_multiple provided',myid
      else
      write(6,*) ' not mpi_thread_multiple provided',myid
      endif

!$OMP PARALLEL PRIVATE(nthreads, tid) NUM_THREADS(2)
!$omp sections
!$omp section
       call mpi_allreduce(A,W,1000,mpi_double_precision,mpi_sum,mpi_comm_world,mpi_err)
!$omp section
       call mpi_allreduce(B,WW,1000,mpi_double_precision,mpi_sum,mpi_comm_world,mpi_err)
!$omp end sections
!$OMP END PARALLEL

       write(6,*) 'W',(w(i),i=1,10)
       write(6,*) 'WW',(ww(i),i=1,10)

      CALL mpi_finalize(mpi_err)
      END 

1 个答案:

答案 0 :(得分:6)

MPI standard禁止在同一个通信器上并发执行(阻塞)集合操作(​​第5.13节“集体通信的正确性”):

  

...

     

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

这里的关键点是:相同的沟通者。没有什么能阻止你开始通过不同的传播者进行同时的集体沟通:

integer, dimension(2) :: comms

call MPI_COMM_DUP(MPI_COMM_WORLD, comms(1), ierr)
call MPI_COMM_DUP(MPI_COMM_WORLD, comms(2), ierr)

!$omp parallel sections num_threads(2)
!$omp section
    call MPI_ALLREDUCE(A, W, 1000, MPI_REAL8, MPI_SUM, comms(1), ierr)
!$omp section
    call MPI_ALLREDUCE(B, WW, 1000, MPI_REAL8, MPI_SUM, comms(2), ierr)
!$omp end parallel sections

call MPI_COMM_FREE(comms(1), ierr)
call MPI_COMM_FREE(comms(2), ierr)

此程序只复制MPI_COMM_WORLD两次。第一个副本用于第一个并行部分,第二个副本用于第二个副本。虽然这两个新的传播者是MPI_COMM_WORLD的副本,但它们是单独的上下文,因此可以对它们进行并发操作。

MPI_COMM_DUP是一项昂贵的操作,因此新创建的通讯器应该在被释放之前尽可能长时间使用。