我有一个Fortran程序,我指定数字数据类型的kind
,试图保持最低精度,无论使用什么编译器来构建程序。例如:
integer, parameter :: rsp = selected_real_kind(4)
...
real(kind=rsp) :: real_var
问题是我使用MPI来并行化代码,我需要确保MPI通信指定具有相同精度的相同类型。我使用以下方法与我的程序中的方法保持一致:
call MPI_Type_create_f90_real(4,MPI_UNDEFINED,rsp_mpi,mpi_err)
...
call MPI_Send(real_var,1,rsp_mpi,dest,tag,MPI_COMM_WORLD,err)
但是,我发现这个MPI例程对于不同的MPI实现并没有特别好的支持,所以它实际上使我的程序不可移植。如果我省略MPI_Type_create
例程,那么我将依赖于标准MPI_REAL
和MPI_DOUBLE_PRECISION
数据类型,但如果该类型与selected_real_kind
不一致,该怎么办?选择最终将由MPI传递的真实类型?我是不是只使用标准real
声明的数据类型,没有kind
属性,如果我这样做,我保证MPI_REAL
和real
始终如一无论编译器和机器如何,都具有相同的精度?
更新:
我创建了一个简单的程序,演示了当我的内部实际具有比MPI_DOUBLE_PRECISION
类型提供的精度更高的精度时看到的问题:
program main
use mpi
implicit none
integer, parameter :: rsp = selected_real_kind(16)
integer :: err
integer :: rank
real(rsp) :: real_var
call MPI_Init(err)
call MPI_Comm_rank(MPI_COMM_WORLD,rank,err)
if (rank.eq.0) then
real_var = 1.123456789012345
call MPI_Send(real_var,1,MPI_DOUBLE_PRECISION,1,5,MPI_COMM_WORLD,err)
else
call MPI_Recv(real_var,1,MPI_DOUBLE_PRECISION,0,5,MPI_COMM_WORLD,&
MPI_STATUS_IGNORE,err)
end if
print *, rank, real_var
call MPI_Finalize(err)
end program main
如果我使用2个内核构建并运行,我会得到:
0 1.12345683574676513672
1 4.71241976735884452383E-3998
现在在selected_real_kind
中将16更改为15,我得到:
0 1.1234568357467651
1 1.1234568357467651
无论使用哪种机器/编译器进行构建,将selected_real_kind(15)
与MPI_DOUBLE_PRECISION
一起使用总是安全的吗?
答案 0 :(得分:3)
使用Fortran 2008内在STORAGE_SIZE
来确定每个数字所需的字节数,并以字节形式发送。请注意,STORAGE_SIZE
以位为单位返回大小,因此您需要除以8以获取字节大小。
此解决方案适用于移动数据,但不能帮助您使用缩减。为此,您必须实现用户定义的缩减操作。如果这对您很重要,我会用详细信息更新我的答案。
例如:
program main
use mpi
implicit none
integer, parameter :: rsp = selected_real_kind(16)
integer :: err
integer :: rank
real(rsp) :: real_var
call MPI_Init(err)
call MPI_Comm_rank(MPI_COMM_WORLD,rank,err)
if (rank.eq.0) then
real_var = 1.123456789012345
call MPI_Send(real_var,storage_size(real_var)/8,MPI_BYTE,1,5,MPI_COMM_WORLD,err)
else
call MPI_Recv(real_var,storage_size(real_var)/8,MPI_BYTE,0,5,MPI_COMM_WORLD,&
MPI_STATUS_IGNORE,err)
end if
print *, rank, real_var
call MPI_Finalize(err)
end program main
我确认此更改可以解决问题,我看到的输出是:
0 1.12345683574676513672
1 1.12345683574676513672
答案 1 :(得分:1)
不是真正的答案,但我们遇到同样的问题并使用类似的东西:
!> Number of digits for single precision numbers
integer, parameter, public :: single_prec = 6
!> Number of digits for double precision numbers
integer, parameter, public :: double_prec = 15
!> Number of digits for extended double precision numbers
integer, parameter, public :: xdble_prec = 18
!> Number of digits for quadruple precision numbers
integer, parameter, public :: quad_prec = 33
integer, parameter, public :: rk_prec = double_prec
!> The kind to select for default reals
integer, parameter, public :: rk = selected_real_kind(rk_prec)
然后我们做了一个初始化例程:
!call mpi_type_create_f90_real(rk_prec, MPI_UNDEFINED, rk_mpi, iError)
!call mpi_type_create_f90_integer(long_prec, long_k_mpi, iError)
! Workaround shitty MPI-Implementations.
select case(rk_prec)
case(single_prec)
rk_mpi = MPI_REAL
case(double_prec)
rk_mpi = MPI_DOUBLE_PRECISION
case(quad_prec)
rk_mpi = MPI_REAL16
case default
write(*,*) 'unknown real type specified for mpi_type creation'
end select
long_k_mpi = MPI_INTEGER8
虽然这不太好,但它运行得相当好,并且似乎可以在Cray,IBM BlueGene和传统Linux集群上使用。 最好的办法是推动网站和供应商在MPI中正确支持这一点。据我所知,它已在OpenMPI中修复,并计划在3.1.1中修复MPICH。请参阅OpenMPI票证3432和3435以及MPICH票证1769和1770。
答案 2 :(得分:0)
怎么样:
integer, parameter :: DOUBLE_PREC = kind(0.0d0)
integer, parameter :: SINGLE_PREC = kind(0.0e0)
integer, parameter :: MYREAL = DOUBLE_PREC
if (MYREAL .eq. DOUBLE_PREC) then
MPIREAL = MPI_DOUBLE_PRECISION
else if (MYREAL .eq. SINGLE_PREC) then
MPIREAL = MPI_REAL
else
print *, "Erorr: Can't figure out MPI precision."
STOP
end if
从那时起使用MPIREAL而不是MPI_DOUBLE_PRECISION。