15

我有一个 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 传递的实际类型MPI_DOUBLE_PRECISION不一致怎么办?selected_real_kind我是否坚持只使用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

现在将 16 更改为 15 英寸selected_real_kind,我得到:

       0   1.1234568357467651     
       1   1.1234568357467651  

selected_real_kind(15)无论使用MPI_DOUBLE_PRECISION什么机器/编译器进行构建 ,使用它是否总是安全的?

4

3 回答 3

4

使用 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  
于 2015-03-13T23:44:06.960 回答
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 票证34323435以及 MPICH 票证17691770

于 2013-11-02T11:57:43.783 回答
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。

于 2014-11-01T12:52:18.543 回答