If I want to use the 64-bit interface I can specify the -i8 compiler Flag for ifort or -fdefault-integer-8 for gfortran.
In MPI however MPI_INTEGER is defined as a fixed 32 bit integer: https://www.ibm.com/support/knowledgecenter/SSFK3V_2.3.0/com.ibm.cluster.pe.v2r3.pe400.doc/am106_pmd.htm
If I have a simple call such as:
MPI_Bcast(buffer, count, MPI_DATATYPE, root, MPI_COMM_WORLD, ierr)
How can I pass MPI_DATATYPE such that it takes the default value? I.e. MPI_INTEGER8 if -i8 is set or MPI_INTEGER4 if not?
I was considering doing it trough a constant, but I don't know what the type of MPI_DATATYPE is. Is it integer(4) just like MPI_COMM_WORLD is?
Edit: I just realized, that different MPI implementations behave differently:
program main
use mpi
integer(4) :: sz
integer(4) :: ierr
call MPI_Init(ierr)
call MPI_Type_size(MPI_INTEGER4, sz, ierr)
write (* ,* ) "4 = ", sz
call MPI_Type_size(MPI_INTEGER8, sz, ierr)
write (* ,* ) "8 = ", sz
call MPI_Type_size(MPI_INTEGER, sz, ierr)
write (* ,* ) "? = ", sz
call MPI_Finalize(ierr)
end program main
IntelMPI:
> $ ./bla.x
4 = 4
8 = 8
? = 4
OpenMPI:
> $ ./bla.x
4 = 4
8 = 8
? = 8
"In MPI however MPI_INTEGER is defined as a fixed 32 bit integer" It is not. That only is true for that particular MPI library you link to. If you link true MPI answer, link to the official MPI specifications.
If you want to use special flags changing the default behaviour so drastically, you would have to compile the MPI library to know about that flags. The implementation may or may not support such a change, but theoretically it should be possible.
There are tutorials on the web that show such compilation for certain MPI implementations. Use your search engine to find links as http://diracprogram.org/doc/release-12/installation/int64/mpi.html http://linuxtoolkit.blogspot.cz/2013/08/building-openmpi-libraries-for-64-bit.html and others.
If you want to be portable without caring about the default integer the compiler decided to use today and needing to have the MPI library synchronized with that, just use integer variables with some fixed storage size like integer(int32) and integer(int64). Those kind constants are defined in the iso_fortran_env module.
Or do not use/change these ugly flags so that you can be sure that the MPI library has the same settings as the compiler's default.
Related
I want to change a large Fortran 90 program to use double precision instead of single precision. I thought the easiest way to do this is to add the following flag to the compiler:
-fdefault-real-8
However, this does not seem to update the MPI commands. For example, I need to change commands like this:
CALL MPI_RECV(x, n, MPI_REAL, rankstart, tag, comm, stat, ierr)
to
CALL MPI_RECV(x, n, MPI_REAL8, rankstart, tag, comm, stat, ierr)
Do you know if there is a compiler flag to change the default value of MPI_REAL to MPI_REAL8? If not, do you know of another way I can easily change the precision of the program without having the manually adjust all the commands in the code myself?
You can use the same flag when configuring your MPI library for compilation. In that case, provided the library is programmed properly, the default real should correspond to the default kind you chose.
The library may request that the default integers and logicals are of the same storage size as the new enlarged real.
Be aware that MPI is an external library. It is compiled separately. It will NOT react to flags that you supply to the compiler when compiling your program. It only knows about the flags that were used to compile the library.
The easiest way to change the precision in a Fortran 90 program is to use a working kind parameter
integer, parameter :: rp = kind(1.d0)
and to declare your real variables as
real(rp) :: x
You can later change the parameter value to any other value as shown at Fortran 90 kind parameter
I have a Fortran program where I specify the kind of the numeric data types in an attempt to retain a minimum level of precision, regardless of what compiler is used to build the program. For example:
integer, parameter :: rsp = selected_real_kind(4)
...
real(kind=rsp) :: real_var
The problem is that I have used MPI to parallelize the code and I need to make sure the MPI communications are specifying the same type with the same precision. I was using the following approach to stay consistent with the approach in my program:
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)
However, I have found that this MPI routine is not particularly well-supported for different MPI implementations, so it's actually making my program non-portable. If I omit the MPI_Type_create routine, then I'm left to rely on the standard MPI_REAL and MPI_DOUBLE_PRECISION data types, but what if that type is not consistent with what selected_real_kind picks as the real type that will ultimately be passed around by MPI? Am I stuck just using the standard real declaration for a datatype, with no kind attribute and, if I do that, am I guaranteed that MPI_REAL and real are always going to have the same precision, regardless of compiler and machine?
UPDATE:
I created a simple program that demonstrates the issue I see when my internal reals have higher precision than what is afforded by the MPI_DOUBLE_PRECISION type:
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
If I build and run with 2 cores, I get:
0 1.12345683574676513672
1 4.71241976735884452383E-3998
Now change the 16 to a 15 in selected_real_kind and I get:
0 1.1234568357467651
1 1.1234568357467651
Is it always going to be safe to use selected_real_kind(15) with MPI_DOUBLE_PRECISION no matter what machine/compiler is used to do the build?
Use the Fortran 2008 intrinsic STORAGE_SIZE to determine the number bytes that each number requires and send as bytes. Note that STORAGE_SIZE returns the size in bits, so you will need to divide by 8 to get the size in bytes.
This solution works for moving data but does not help you use reductions. For that you will have to implement a user-defined reduction operation. If that's important to you, I will update my answer with the details.
For example:
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
I confirmed that this change corrects the problem and the output I see is:
0 1.12345683574676513672
1 1.12345683574676513672
Not really an answer, but we have the same problem and use something like this:
!> 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)
And then have an initialization routine where we do:
!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
While this is not nice, it works reasonably well, and seems to be usable on Cray, IBM BlueGene and conventional Linux Clusters.
Best thing to do is push sites and vendors to properly support this in MPI. As far as I know it has been fixed in OpenMPI and planned to be fixed in MPICH by 3.1.1. See OpenMPI Tickets 3432 and 3435 as well as MPICH Tickets 1769 and 1770.
How about:
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
and use MPIREAL instead of MPI_DOUBLE_PRECISION from then on.
I am trying to switch to use mpi for some old fortran codes I have. I got some strange errors when compiling the code.
Error: There is no specific subroutine for the generic 'mpi_type_indexed' at (1)
when I try to switch to 'use mpi' in the code. If I use 'include 'mpif.h'' then the program got compiled and is able to run correctly.
I have written a compact example to verify the program. Both the code and my example are compiled under gcc/8.1.0 and openmpi/3.1.2.
program bt
use mpi
implicit none
!include 'mpif.h'
contains
subroutine read_me()
implicit none
integer :: my_n, my_disp, my_type
integer :: ierr
my_n = 2
my_disp = 4
call MPI_Type_indexed(1, my_n, my_disp, MPI_INTEGER, my_type, ierr)
end subroutine
end program
compile it with no flag: mpif90 bt.F90
With use mpi committed and include 'mpif.h' uncommitted, everything works fine.
With use mpi uncommitted and include 'mpif.h' committed, I got error says
bt.F90:23:67:
call MPI_Type_indexed(1, my_n, my_disp, MPI_INTEGER, my_type, ierr)
1
Error: There is no specific subroutine for the generic 'mpi_type_indexed' at (1)
As indicated in the comments the "problem" that has occurred is that because you have used the module rather than the include file an interface is now in scope, and the compiler can now detect that you are trying to call MPI_Type_indexed with incorrect arguments, as the 2nd and 3rd arguments should be arrays - take a look at https://www.mpi-forum.org/docs/mpi-3.1/mpi31-report/node79.htm#Node79 to see what the interface should be.
Looking at your example it looks as though the original author was assuming that a scalar and a 1 element array are the same thing - this is not the case as the former is rank 0 and the later rank 1. I say this as the first argument specifies how big the arrays should be, and in your case this has the value 1. Thus the 2nd and 3rd arguments should be single element arrays, rather than the scalars you have. The simplest solution, as these arguments are Intent( In ), is to put them in square brackets acting as an array constructor
call MPI_Type_indexed(1, [ my_n ], [ my_disp ], MPI_INTEGER, my_type, ierr)
I am refactoring F77 program to more recent Fortran standard (90 or even newer).
I have a module where some variables defined. These variables are currently put into common block, because in external subroutine all these variables are broadcasted using only one MPI_BCAST call and exploiting contiguous storage of variables in this common block.
module foo
implicit none
integer :: a,b,c,d
real*8 :: r,t,p
common/com/ a,b,c,d,r,t,p
end module foo
subroutine bar
...
com_length = 4*4 + 3*8 ! 4 integers + 3 real(8)
! bcasting 'com' common block, i.e. all variables at once
call mpi_bcast(a,com_length,mpi_byte,root,comm,ierr)
...
end subroutine bar
Problem is that length of common block com_length is calculated manually and error prone. If COMMON block definition is missing, debug will take ages because even valgrind won't notice OOB.
On the other hand, calling MPI_BCAST separately for each variable will negatively impact performance.
I will appreciate your suggestions on how to refactor this.
You could do it in 2 MPI_BCAST calls.
CALL MPI_BCAST([a, b, c, d], 4, MPI_INTEGER, root, MPI_COMM_WORLD, ierr)
CALL MPI_BCAST([t, r, p], 3, MPI_DOUBLE_PRECISION, root, MPI_COMM_WORLD, ierr)
The 4 and 3 may not be exactly right, but the idea is still the same: group your like-variables as an array and broadcast them.
How can I read and write to the standard input, output and error streams stdin, stdout and stderr in Fortran? I've heard writing to stderr, for example, used to be write(5, fmt=...), with 5 the unit for stderr, and I know the way to write to stdout is to use write(*, fmt=...).
How do I read and write to the standard input and output units with the ifort compiler?
Compiler version:
Intel(R) Fortran Compiler for applications running on Intel(R) 64, Version 10.0 Build 20070426 Package ID: l_fc_p_10.0.023 Copyright (C) 1985-2007 Intel Corporation. All rights reserved
If you have a Fortran 2003 compiler, the intrinsic module iso_fortran_env defines the variables input_unit, output_unit and error_unit which point to standard in, standard out and standard error respectively.
I tend to use something like
#ifdef f2003
use, intrinsic :: iso_fortran_env, only : stdin=>input_unit, &
stdout=>output_unit, &
stderr=>error_unit
#else
#define stdin 5
#define stdout 6
#define stderr 0
#endif
in my input/output routines. Although this of course means preprocessing your source file (to do this with ifort, use the -fpp flag when compiling your source code or change the source file extension from .f to .F or from .f90 to .F90).
An alternative to this would be to write your own, non-intrinsic, iso_fortran_env module (if you don't have a Fortran 2003 compiler), as discussed here (this link has died since this answer was posted). In this example they use a module:
module iso_fortran_env
! Nonintrinsic version for Lahey/Fujitsu Fortran for Linux.
! See Subclause 13.8.2 of the Fortran 2003 standard.
implicit NONE
public
integer, parameter :: Character_Storage_Size = 8
integer, parameter :: Error_Unit = 0
integer, parameter :: File_Storage_Size = 8
integer, parameter :: Input_Unit = 5
integer, parameter :: IOSTAT_END = -1
integer, parameter :: IOSTAT_EOR = -2
integer, parameter :: Numeric_Storage_Size = 32
integer, parameter :: Output_Unit = 6
end module iso_fortran_env
As noted in other answers, 0, 5 and 6 are usually stderr, stdin and stdout (this is true for ifort on Linux) but this is not defined by the Fortran standard. Using the iso_fortran_env module is the correct way to portably write to these units.
The Fortran standard doesn't specify which units numbers correspond to stdin/out/err. The usual convention, followed by e.g. gfortran, is that stderr=0, stdin=5, stdout=6.
If your compiler supports the F2003 ISO_FORTRAN_ENV intrinsic module, that module contains the constants INPUT_UNIT, OUTPUT_UNIT, and ERROR_UNIT allowing the programmer to portably retrieve the unit numbers for the preconnected units.
It's actually 0 for stderr. 5 is stdin, 6 is stdout.
For example:
PROGRAM TEST
WRITE(0,*) "Error"
WRITE(6,*) "Good"
END PROGRAM TEST
Gives:
./a.out
Error
Good
while
./a.out 2> /dev/null
Good
I would store a PARAMETER that is STDERR = 0 to make it portable, so if you hit a platform that is different you can just change the parameter.
This example was compiled and run with ifort 12.1.1.256, 11.1.069, 11.1.072 and 11.1.073.
The standard way to write to stdout in Fortran is to put an asterisk instead of the unit number, i.e.,
WRITE(*,fmt) something
or to simply use
PRINT fmt,something
Similarly, the standard way to read from stdin is
READ(*,fmt) something
There is no standard way to write to stderr unless you use ERROR_UNIT from the ISO_FORTRAN_ENV module, which requires Fortran 2003 or later.
Unit numbers 0, 5 and 6 will certainly work in the ifort compiler (and also in some other Fortran compilers), but keep in mind they are compiler-dependent.