Fortran下MPI_ALLreduce处理2字节整数的跨编译器兼容问题
嘿,我碰到过类似的问题!你这儿的核心矛盾是Intel MPI对MPI_TYPE_CREATE_F90_INTEGER创建的自定义F90整数类型,没有内置支持MPI_SUM操作,而OpenMPI在这方面做了额外的兼容处理。要实现能跑在Gfortran+OpenMPI、Intel等多数编译器/MPI环境下的标准写法,给你几个靠谱的方案:
方案一:直接用MPI预定义的2字节整数类型(最推荐)
Fortran里SELECTED_INT_KIND(2)对应的2字节整数,刚好和MPI标准里的MPI_SHORT类型匹配(MPI_SHORT对应C的short,通常就是16位2字节)。直接用这个预定义类型替代你自定义的int2type,MPI_SUM肯定能正常工作,因为这是MPI标准规定的基本数据类型:
INTEGER, PARAMETER :: SIK2 = SELECTED_INT_KIND(2) INTEGER(SIK2) :: s_save(dim) ! 跳过自定义类型创建,直接用MPI_SHORT CALL MPI_ALLreduce(MPI_IN_PLACE, s_save, nkpt_in, MPI_SHORT, MPI_SUM, world_comm, ierr)
要是你不确定SIK2是不是真的对应2字节,可以加一行代码验证:
PRINT *, "Size of SIK2 integer: ", SIZEOF(s_save(1))
只要输出是2,就可以放心用MPI_SHORT——你的数值范围是1到48,完全在MPI_SHORT的表示范围内。
方案二:给自定义类型手动注册求和操作(适合必须用F90类型的场景)
如果你因为项目要求必须使用MPI_TYPE_CREATE_F90_INTEGER生成的类型,可以手动给这个类型注册MPI_SUM操作。步骤稍微繁琐一点,但能完全贴合你的自定义类型:
INTEGER :: int2type, ierr, custom_sum_op EXTERNAL :: short_sum_func ! 先创建并提交自定义F90整数类型 CALL MPI_TYPE_CREATE_F90_INTEGER(SIK2, int2type, ierr) CALL MPI_TYPE_COMMIT(int2type, ierr) ! 注册自定义的求和操作(第二个参数.TRUE.表示操作是可结合的) CALL MPI_OP_CREATE(short_sum_func, .TRUE., custom_sum_op, ierr) ! 使用注册后的操作执行Allreduce CALL MPI_ALLreduce(MPI_IN_PLACE, s_save, nkpt_in, int2type, custom_sum_op, world_comm, ierr) ! 用完记得释放资源 CALL MPI_OP_FREE(custom_sum_op, ierr) CALL MPI_TYPE_FREE(int2type, ierr) ! 自定义的求和函数,必须符合MPI要求的参数格式 SUBROUTINE short_sum_func(invec, inoutvec, len, dtype) INTEGER(SIK2), INTENT(IN) :: invec(*) INTEGER(SIK2), INTENT(INOUT) :: inoutvec(*) INTEGER, INTENT(IN) :: len, dtype INTEGER :: i DO i = 1, len inoutvec(i) = inoutvec(i) + invec(i) END DO END SUBROUTINE short_sum_func
注意这个求和函数的参数顺序和类型必须严格符合MPI的要求,不然会出奇怪的问题。
方案三:临时转换为标准类型求和(兜底兼容方案)
如果上面两种方法都不适用,还有个兼容性拉满的兜底方案:先把2字节数组转换成MPI肯定支持的标准类型(比如4字节的INTEGER(4)对应MPI_INT),求和后再转换回去:
INTEGER, PARAMETER :: SIK2 = SELECTED_INT_KIND(2) INTEGER(SIK2) :: s_save(dim) INTEGER :: temp_arr(dim) ! 把2字节整数转成4字节整数(你的数值范围1-48,完全不会溢出) temp_arr = INT(s_save, KIND=4) ! 用MPI_INT执行求和,这是所有MPI实现都支持的 CALL MPI_ALLreduce(MPI_IN_PLACE, temp_arr, nkpt_in, MPI_INT, MPI_SUM, world_comm, ierr) ! 转换回2字节整数 s_save = INT(temp_arr, KIND=SIK2)
这个方法的缺点是需要额外的内存存临时数组,但胜在绝对兼容,不管什么MPI实现都能跑。
总的来说,方案一是最优解——简单、高效、完全符合MPI标准,跨编译器兼容性最好。
内容的提问来源于stack exchange,提问作者sponce




