Fortran 2008中MPI_Op_create与MPI_Reduce自定义操作实现求助
问题描述
已在C语言中实现基于自定义操作的MPI归约,但在Fortran 2008中尝试时遇到困难。现有代码使用MPI_SUM可正常将4个MPI进程的rank值求和为全局总和6,但自定义操作实现的代码运行结果为3,寻求正确的Fortran 2008实现方案。
可用的MPI_SUM正确代码
program main use mpi_f08 implicit none integer :: ierror, isize, irank integer :: local_value, global_value call MPI_INIT(ierror) call MPI_COMM_SIZE(MPI_COMM_WORLD, isize) call MPI_COMM_RANK(MPI_COMM_WORLD, irank) if (irank == 0) then local_value = irank global_value = 0 call MPI_Reduce(local_value, global_value, 1, MPI_INT, MPI_SUM, 0, MPI_COMM_WORLD, ierror) print *, "global_value", global_value else local_value = irank call MPI_Reduce(local_value, global_value, 1, MPI_INT, MPI_SUM, 0, MPI_COMM_WORLD, ierror) end if call MPI_FINALIZE(ierror) end program main
有问题的自定义操作代码
program main use mpi implicit none integer :: local_value, global_sum integer :: ierr, rank, size integer :: my_op call MPI_Init(ierr) call MPI_Comm_size(MPI_COMM_WORLD, size, ierr) call MPI_Comm_rank(MPI_COMM_WORLD, rank, ierr) call MPI_Op_create(custom_sum_op, .true., my_op, ierr) if (rank == 0) then local_value = rank global_sum = 0 call MPI_Reduce(local_value, global_sum, 1, MPI_INT, my_op, 0, MPI_COMM_WORLD, ierr) print *, "Global sum: ", global_sum else local_value = rank call MPI_Reduce(local_value, global_sum, 1, MPI_INT, my_op, 0, MPI_COMM_WORLD, ierr) endif call MPI_Finalize(ierr) contains subroutine custom_sum_op(res, a) integer, intent(inout) :: res integer, intent(in) :: a res = a + res end subroutine custom_sum_op end program main
解决方案与错误分析
错误原因
- 自定义操作子例程接口不符合MPI规范:Fortran的MPI自定义操作必须使用固定的四参数接口,而非仅两个参数。MPI需要通过额外参数处理数据长度、类型等信息,即使单元素归约也不能省略。
- 未释放自定义操作对象:创建的自定义操作
my_op未调用MPI_Op_free释放,可能导致资源泄漏。
正确的Fortran 2008实现
program main use mpi_f08 implicit none integer :: local_value, global_sum integer :: ierr, rank, size type(MPI_Op) :: my_op ! 使用mpi_f08的类型而非整数句柄 call MPI_Init(ierr) call MPI_Comm_size(MPI_COMM_WORLD, size, ierr) call MPI_Comm_rank(MPI_COMM_WORLD, rank, ierr) ! 创建自定义归约操作,求和为结合性操作,设置为.true. call MPI_Op_create(custom_sum_op, .true., my_op, ierr) local_value = rank if (rank == 0) then global_sum = 0 call MPI_Reduce(local_value, global_sum, 1, MPI_INT, my_op, 0, MPI_COMM_WORLD, ierr) print *, "Global sum: ", global_sum else ! 非根进程的recvbuf可使用任意变量,此处沿用global_sum不影响结果 call MPI_Reduce(local_value, global_sum, 1, MPI_INT, my_op, 0, MPI_COMM_WORLD, ierr) endif ! 释放自定义操作对象 call MPI_Op_free(my_op, ierr) call MPI_Finalize(ierr) contains ! 符合MPI规范的自定义操作子例程接口 subroutine custom_sum_op(invec, inoutvec, len, datatype) use mpi_f08 implicit none integer, intent(in) :: invec(*) ! 输入数据向量 integer, intent(inout) :: inoutvec(*) ! 累加结果向量 integer, intent(in) :: len ! 数据元素长度 type(MPI_Datatype), intent(in) :: datatype ! 数据类型 integer :: i ! 对每个元素执行求和累加 do i = 1, len inoutvec(i) = inoutvec(i) + invec(i) end do end subroutine custom_sum_op end program main
关键修正点说明
- 使用
mpi_f08模块:提供Fortran 2008兼容的类型安全MPI接口,替代旧版的整数句柄。 - 修正自定义操作接口:必须包含
invec、inoutvec、len、datatype四个参数,MPI会自动传递这些信息,确保归约逻辑正确执行。 - 释放自定义操作:调用
MPI_Op_free释放创建的操作对象,避免资源泄漏。 - 简化代码逻辑:将
local_value = rank移到条件判断外,代码更简洁规范。
内容的提问来源于stack exchange,提问作者Jay
相关产品推荐
相关产品推荐

