如何以可移植的方式保持Fortran MPI程序的精度?
我有一个Fortran程序,其中我指定了数值数据类型的如何以可移植的方式保持Fortran MPI程序的精度?,fortran,mpi,precision,Fortran,Mpi,Precision,我有一个Fortran程序,其中我指定了数值数据类型的种类,试图保持最低的精度水平,而不管使用什么编译器来构建程序。例如: integer, parameter :: rsp = selected_real_kind(4) ... real(kind=rsp) :: real_var program main use mpi implicit none integer, parameter :: rsp = selected_real_kind(16) integ
种类
,试图保持最低的精度水平,而不管使用什么编译器来构建程序。例如:
integer, parameter :: rsp = selected_real_kind(4)
...
real(kind=rsp) :: real_var
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
问题是我使用了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\u Type\u create
例程,那么我就只能依赖标准的MPI\u REAL
和MPI\u DOUBLE\u PRECISION
数据类型,但是如果该类型与selected\u REAL\u kind
选择的真正类型不一致怎么办?我是否仅仅使用了数据类型的标准real
声明,没有kind
属性?如果我这样做,我是否可以保证MPI\u real
和real
始终具有相同的精度,而不管编译器和机器是什么
更新:
我创建了一个简单的程序,演示了当我的内部实数的精度高于MPI\u DOUBLE\u 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
现在将16英寸更改为15英寸选定的\u real\u kind
,我得到:
0 1.1234568357467651
1 1.1234568357467651
无论使用何种机器/编译器进行构建,使用
selected\u real\u kind(15)
和MPI\u DOUBLE\u PRECISION
总是安全的吗 这不是一个真正的答案,但我们也有同样的问题,并使用类似的方法:
!> 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、IBMBlueGene和传统的Linux集群上使用。
最好的办法是推动站点和供应商在MPI中正确支持这一点。据我所知,它已在OpenMPI中修复,并计划在3.1.1中在MPICH中修复。请参阅OpenMPI票证和以及MPICH票证和。如何:
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
从那时起,使用MPI\u双精度而不是MPI\u双精度。使用Fortran 2008内部
存储大小来确定每个数字所需的字节数并作为字节发送。请注意,STORAGE\u SIZE
返回以位为单位的大小,因此需要除以8才能获得以字节为单位的大小
此解决方案适用于移动数据,但不能帮助您使用数据缩减。为此,您必须实现用户定义的缩减操作。如果这对你很重要,我会用细节更新我的答案
例如:
integer, parameter :: rsp = selected_real_kind(4)
...
real(kind=rsp) :: real_var
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
我使用的代码将其工作精度定义为wp=selected\u real\u kind(14,40)
,然后在MPI调用中使用MPI\u DOUBLE\u precision
。据我所知,它没有问题。我添加了一个简单的例子,说明我需要避免什么问题。所以我想知道,如果我明确定义它们的种类
,我的real
s是否总是与MPI\u DOUBLE\u精度
兼容?最后我检查了一下,使用16表示四精度(因此是-3998范围),它不应该与MPI\u DOUBLE\u精度
兼容。只要使用7到15(包括7到15),就可以使用MPI\u DOUBLE\u PRECISION
。是的,你是对的。但是MPI\u DOUBLE\u PRECISION
是否总是意味着无论系统/编译器如何,MPI通信的精度都保持在7到15位之间?换句话说,如果我将我的real
s指定为14位精度,然后使用MPI\u DOUBLE\u precision
进行MPI通信,我是否需要担心得到像我发布的那样的结果,即数字在接收端显示为垃圾?除非使用选定的\u real\u kind
,您可以使用ISO_FORTRAN_ENV
并使用类型real32
和real64
(位数)可移植地指定单精度和双精度实数。您是对的,我的代码使用OpenMPI 1.4.3运行良好,但使用MPICH时出现问题。我想我最终还是会使用默认的MPI Fortran数据类型,而不是尝试手动设置精度。