【问题标题】:running mpi subroutine in fortran program在 fortran 程序中运行 mpi 子例程
【发布时间】:2018-11-05 22:51:20
【问题描述】:

我想运行一个 Fortran 程序,该程序调用一个我想与 MPI 并行化的子例程。我知道这听起来很复杂,但我希望能够为每个调用指定进程数。我想要使​​用的是这样的结构:

program my_program

implicit none

!Define variables

nprocs =  !formula for calculating number of processes.

call my_subroutine(output,nprocs,other input vars)

end my_program

我想运行my_subroutine,效果和这个一样:

mpirun -n nprocs my_subroutine.o

my_subroutine 使用“其他输入变量”编译的位置。

这可能吗?


这是一个简单的例子。我尝试编译如下: $ mpif90 -o my_program WAVE_2D_FP_TUNER_mpi.f90 randgen.f SIMPLE_ROUTINE.f90 我尝试像这样运行它: $ mpirun -np (1 or 2) my_program


PROGRAM WAVE_2D_FP_TUNER_mpi
USE MPI
IMPLICIT NONE

REAL(KIND=8) :: T,PARAM(1:3),Z,ZBQLU01
REAL(KIND=8) :: ERRORS,COSTS,CMAX,CMAX_V(1:1000),THRESHOLD,Z_MIN,Z_MAX
REAL(KIND=8) :: U,S,R(1:6),MATRIX(1:15)
INTEGER :: EN,INC,I,J,M,P
INTEGER :: NPROCS,IERR

!0.8,-0.4,0.4,10,4,4,7 -- [0.003,0.534]
!0.8,-0.2,0.2,10,4,4,7 -- [0.190,0.588]
CALL MPI_INIT(IERR)
CALL MPI_COMM_SIZE(MPI_COMM_WORLD,NPROCS,IERR)

THRESHOLD = 0.D0
EN = 81
INC = 1
Z_MIN = -2.D-1; Z_MAX = 2.D-1

T = 1.D0
PARAM(1) = 10.D0; PARAM(2) = 4.D0; PARAM(3) = 4.D0

CMAX = 7.D0 !Max that wave speed could possibly be.

CALL ZBQLINI(0.D0)

OPEN(UNIT = 1, FILE = "TUNER_F.txt")
WRITE(1,*) 'Grid Size: '
WRITE(1,*) T/(EN-1)

DO P = 1,15
    S = 0
    Z = Z_MIN + (1.d0/(15-1))*dble((P-1))*(Z_MAX - Z_MIN)
    WRITE(1,*) 'Z: ',Z
    DO I = 1,1000
        DO J = 1,6
            R(J) = ZBQLU01(0.D0)
        END DO
        !CALL PDE_WAVE_F_mpi(T,PARAM,R,Z,CMAX,EN,INC,NPROCS,U)
        CALL SIMPLE_ROUTINE(T,PARAM,R,Z,CMAX,EN,INC,NPROCS,U)
        IF (U<=threshold) THEN
        S = S + 1.D0
        ELSE 
        S = S + 0.D0
        END IF
    END DO
    MATRIX(P) = (1.D0/1000)*S
END DO

DO I = 1,15
    WRITE(1,*) MATRIX(I)
END DO

PRINT *,MINVAL(MATRIX)
PRINT *,MAXVAL(MATRIX)

CLOSE(1)

CALL MPI_FINALIZE(IERR)

END PROGRAM WAVE_2D_FP_TUNER_mpi

这是我希望与 mpi 并行化的子例程。


SUBROUTINE SIMPLE_ROUTINE(T,PARAM,R,Z,CMAX,EN,INC,NPROCS,U)
! Outputs scalar U = T*Z*CMAX*INC*SUM(PARAM)*SUM(R)*SUM(Y)
USE MPI

IMPLICIT NONE

REAL(KIND=8), INTENT(IN) :: T,PARAM(1:3),R(1:6),Z,CMAX
INTEGER, INTENT(IN) :: EN,INC
INTEGER, INTENT(IN) :: NPROCS
REAL(KIND=8), INTENT(OUT) :: U
REAL(KIND=8) :: H,LOCAL_SUM,SUM_OF_X
REAL(KIND=8), DIMENSION(:), ALLOCATABLE :: X
INTEGER :: PX,PX_MAX,NXL,REMX,IX_OFF,P_LEFT,P_RIGHT
INTEGER :: J
INTEGER :: IERR,MYID

! Broadcast nprocs handle to all processes in MPRI_COMM_WORLD
CALL MPI_BCAST(&NPROCS, NPROCS, MPI_INT, 0, MPI_COMM_WORLD,IERR)
! Create subcommunicator SUBCOMM  (Do not know how to define WORLD_GROUP?)
CALL MPI_COMM_SPLIT(MPI_COMM_WORLD,WORLD_GROUP,SUBCOMM,IERR)
! Assign IDs to processes in SUBCOMM
CALL MPI_COMM_RANK(SUBCOMM,MYID,IERR)
! Give NPROCS - 1 to SUBCOMM
CALL MPI_COMM_SIZE(SUBCOMM,NPROCS-1,IERR)

H = 2.D0/(EN-1)

! LABEL THE PROCESSES FROM 1 TO PX_MAX.
PX = MYID + 1
PX_MAX = NPROCS
! SPLIT UP THE GRID IN THE X-DIRECTION.
NXL = EN/PX_MAX !nxl = 10/3 = 3
REMX = EN-NXL*PX_MAX !remx = 10-3*3 = 1
IF (PX .LE. REMX) THEN !for px = 1,nxl = 3
    NXL = NXL+1 !nxl = 4
    IX_OFF = (PX-1)*NXL !ix_off = 0
ELSE
    IX_OFF = REMX*(NXL+1)+(PX-(REMX+1))*NXL !for px = 2 and px = 3, ix_off = 1*(3+1)+(2-(1+1))*3 = 4, ix_off = 1*(3+1)+(3-(1+1))*3 = 7
END IF

! ALLOCATE MEMORY FOR VARIOUS ARRAYS.
ALLOCATE(X(0:NXL+1))
X(:) = (/(-1.D0+DBLE(J-1+IX_OFF)*H, J=1,EN)/)
LOCAL_SUM = SUM(X(1:NXL))
CALL MPI_REDUCE(LOCAL_SUM,SUM_OF_X,1,&
          MPI_DOUBLE_PRECISION,MPI_SUM,&
          0,MPI_COMM_WORLD,IERR)

U = T*Z*CMAX*INC*SUM(PARAM)*SUM(R)*SUM_OF_X

DEALLOCATE(X)

CALL MPI_COMM_FREE(SUBCOMM,IERR)

CALL MPI_BARRIER(MPI_COMM_WORLD,IERR)

END SUBROUTINE SIMPLE_ROUTINE

最终,我希望能够更改子例程中使用的处理器数量,我希望从 EN 的值计算 nprocs。

【问题讨论】:

  • 为什么你认为你需要这个?我打赌你不会。
  • 你想要的真的很奇怪。对于线程来说这很自然,但对于 MPI 则不然。 Giles 在下面的回答是完全正确的。
  • 我不认为我正在尝试做的事情很奇怪。我最终试图解决一个由 EN 值确定的具有不同网格细化的 pde。我想在运行时根据 EN 的值增加在 pde 求解器上工作的处理器数量。这应该可以防止代码在非常精细的网格尺寸下减慢很多,对吧?
  • MPI 通常不是这样使用的。反正运行进程的总数不是由cpu的数量固定的吗?

标签: fortran mpi


【解决方案1】:

一种简单的方法是使用最大进程数启动 MPI 应用程序。

然后my_subroutine 将首先MPI_Bcast(&amp;nprocs, ...)MPI_COMM_SPLIT(MPI_COMM_WORLD, ..., &amp;subcomm) 以便创建一个子通信器subcommnprocs (您可以使用MPI_UNDEFINED,因此“其他”通信器将是MPI_COMM_NULL

然后,属于subcomm 的 MPI 任务将执行计算。

最后,MPI_Comm_free(&amp;subcomm)MPI_Barrier(MPI_COMM_WORLD)

从性能的角度来看,创建笔记子通信器的成本可能很高,但与计算时间相比,希望不会显着。 如果没有,您宁愿修改您的算法,以便它可以让 nprocs 任务完成这项工作,而其他任务则等待。

另一种方法是使用一个 MPI 任务启动您的应用程序,MPI_Comm_spawn()nprocs-1 任务,合并内部通信器,执行计算,并终止生成的任务。 任务创建的开销更为重要,您的资源管理器可能不完全支持这一点,因此我不建议使用此选项。

【讨论】:

  • 好的。让我想想这个。但是如何控制 nprocs 是什么?
  • 什么意思?你没有计算它的公式吗?如果您的意思是以mpirun 开头的任务数,那么它应该是您的公式将返回的最大值。
  • 此解决方案是所提问题的正确答案。我想知道 OP 是否真的知道他们的要求,不过......
  • 我想我很困惑,因为我不想从最大进程数开始。例如,我想用 nprocs = 1 运行 my_subroutine 几千次,然后用 nprocs = 2,然后用 nprocs = 4、8、16 等。希望我说得通。
  • 我了解您的要求。这需要MPI_Comm_spawn(),但我首先建议不要这样做。 MPI 方式是始终启动 4 个任务,并有 1 个工作/3 个等待,然后 2 个工作/2 个等待,...或者简单地调用 mpirun 4 次不同的任务计数。我完全理解这乍一看并不直观,但这是我推荐您使用 MPI 的方式。
猜你喜欢
  • 2014-08-24
  • 1970-01-01
  • 2015-04-14
  • 1970-01-01
  • 1970-01-01
  • 2019-08-29
  • 1970-01-01
  • 2021-02-11
  • 2022-01-08
相关资源
最近更新 更多