我在复制的混合MPI/OpenMP代码中发现了一个问题以下面引用的代码中最简单的形式。我正在使用2个线程每个MPI等级。这两个线程随后被用于OpenMP"Section"要进行多次计算,其中之一是对两个不同的向量a和B进行"mpi_allreduce"调用,这两个向量的结果存储在W和WW中。问题是每次我运行程序时我最终得到了不同的输出。我认为MPI调用重叠并且缩减的阵列W和WW即使在它们有不同的名字,但我不确定。关于如何克服的任何评论这个问题是受欢迎的。
详细信息:MPI线程级别在代码中初始化为MPI_THRAD_MULTIPLE但我也尝试过串行和漏斗(同样的问题)。
我编译了mpiifort-openmp allreduce_omp_mpi.f90和运行我使用的:
导出OMP_NUM_THREADS=2mpirun-np3/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
最后,在多线程实现中,一个进程可以有多个并发执行的集体通信调用。在这些情况下,用户有责任确保同一个通信器不会被同一进程中的两个不同的集体通信调用同时使用。
这里的关键点是:相同的通信器。没有什么可以阻止您通过不同的通信机启动并发的集体通信:
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
是一个昂贵的操作,因此新创建的通信器在释放之前应该使用尽可能长的时间。