Hi!
I want to convert the input/output of my program to MPI-IO routines. To
write the same data, as is written in the already implement textmode
routines, I need a custom datatype consisting of several integers and reals.
While investigating the functionality, I created a module, which cares for
the type, and a small testprogram, which performs a mpi_scatter, a parallel
write with mpi_file_write and finally a single read from the master with
mpi_file_read, to compare the data with the original dataset.
My testtype for this particular problem is defined as an 8 byte real
followed by a 4 byte integer. This way, the datasize is 12 byte, while the
extent differs for different compilers (gfortran has an extent of 16 byte,
while ifort has an extent of 12 byte). I know, that I can fix the extent
with the help of mpi_type_create_resized after measuring the distance of
the elements in an array, but this doesn't show the strange behaviour, I
don't understand.
My problem is, that I don't really understand the way, how mpi_file_read
uses this extent to perform file-io on arrays of this data. On the one
hand, the data, which is written to the file, is only the data, which is
actually used, i.e. no padding is written to the file and thus, only the
mpi_type_size is interesting for the writing. On the other hand, the
missing padding in the extent of ifort causes mpi_file_read to skip some of
the last datasets. In my opinion, this would make some sense, if the
padding of the datasets would cause corruption on the read data after the
first padding, but this is not the case. If you execute the appended
program with 4 processes, the last two of eight datasets stay uninitialized
(keep their values of -1 for each member) what suggests that the amount of
data is somehow related to the extent rather than the type_size.
What is the problem here? Do I misunderstand, how these routines should
work?
Best regards,
Stefan
PS: Does the stat(1) variable mean anything? It returns 0 all the time.
### CODE ###
module mpiio
!use mpi
implicit none
include 'mpif.h'
private
public :: r1i1_iotype
public :: r1i1_mpi
public :: init_mpiio, get_mpiio_dtypes
type, bind(c) :: r1i1_iotype
real(8) :: r
integer :: i
end type
integer :: r1i1_mpi
contains
! initializer, which creates datatypes for all types in this module
subroutine init_mpiio()
r1i1_mpi = get_r1i1_type()
end subroutine
! inforoutine, which prints information about all types in this module
subroutine get_mpiio_dtypes()
write(*,"(A80)") "=== Available Datatypes ================" // &
& "========================================"
call get_dtype_info(r1i1_mpi)
write(*,"(A80)") "========================================" // &
& "========================================"
end subroutine
! generic routine, which prints information about a datatype
subroutine get_dtype_info(dt)
integer :: dt
integer :: ierror, nl, i, sz
character(LEN=63) :: type_name
integer(mpi_address_kind) :: lb, extent
integer(mpi_address_kind) :: true_lb, true_extent
integer :: comb
integer :: num_int, num_addr, num_dt
integer, dimension(:), allocatable :: aob
integer(mpi_address_kind), dimension(:), allocatable :: aod
integer, dimension(:), allocatable :: aot
! get name of datatype
call mpi_type_get_name(dt, type_name, nl, ierror)
write(*,*) "Name: ", type_name
! get start address and extent
call mpi_type_get_extent(dt, lb, extent, ierror)
call mpi_type_get_true_extent(dt, true_lb, true_extent, ierror)
write(*,*) "lb: ", lb, " true lb: ", true_lb
write(*,*) "extent: ", extent, " true extent: ", true_extent
! get contents
call mpi_type_get_envelope(dt, num_int, num_addr, num_dt, comb,
ierror)
allocate(aob(num_int))
allocate(aod(num_addr))
allocate(aot(num_dt))
call mpi_type_get_contents(dt, num_int, num_addr, num_dt, aob, aod,
aot, ierror)
write(*,*) "consists of: ", num_int - 1, aob(1)
do i = 1, num_int - 1
call mpi_type_get_name(aot(i), type_name, nl, ierror)
write(*,*) i, ":", aob(i+1), aod(i), trim(type_name)
end do
call mpi_type_size(dt, sz, ierror)
write(*,*) "data size: ", sz
end subroutine
function get_r1i1_type() result(dt)
! datatype
integer :: dt
type(r1i1_iotype) :: t
integer :: ierror
integer(mpi_address_kind) :: a_base, a_r, a_i
! number of blocks
integer, parameter :: cnt = 2
! elements in each block
integer, dimension(cnt) :: aob
! types in each block
integer, dimension(cnt) :: aot
! displacements of each block
integer(mpi_address_kind), dimension(cnt) :: aod
aob = [1, 1]
aot = [mpi_double_precision, mpi_integer]
! get base address and address of members
call mpi_get_address(t, a_base, ierror)
call mpi_get_address(t%r, a_r, ierror)
call mpi_get_address(t%i, a_i, ierror)
! convert to offsets/displacements
a_r = a_r - a_base
a_i = a_i - a_base
! displacements
aod = [a_r, a_i]
! create type
call mpi_type_create_struct(cnt, aob, aod, aot, dt, ierror)
! name the type
call mpi_type_set_name(dt, "r1i1", ierror)
! commit type
call mpi_type_commit(dt, ierror)
end function
end module
program test
use mpiio
implicit none
include 'mpif.h'
type(r1i1_iotype), dimension(:), allocatable :: v, w
integer :: dt
integer :: myrank, mpisize
integer :: ierror
real(8) :: ranarray(2)
integer :: i, n, m
integer :: fh
integer, dimension(mpi_status_size) :: stat
integer(mpi_offset_kind) :: fp, fs, fs2
integer :: sz
logical :: correct
! problem size
m = 2
! initializer
call mpi_init(ierror)
call mpi_comm_rank(mpi_comm_world, myrank, ierror)
call mpi_comm_size(mpi_comm_world, mpisize, ierror)
! initialize datatypes and print info
call init_mpiio()
if (myrank == 0) then
call get_mpiio_dtypes()
endif
dt = r1i1_mpi
! synchronize to avoid mess of output
call mpi_barrier(mpi_comm_world, ierror)
! create data
n = mpisize * m
allocate(w(m))
! root process will create a batch of values
if (myrank == 0) then
allocate(v(n))
do i = 1, n
call random_number(ranarray)
v(i) % r = ranarray(1)
v(i) % i = int(huge(v(i)%i) * ranarray(2))
end do
end if
! scatter across the processes
call mpi_scatter(v, m, dt, w, m, dt, 0, mpi_comm_world, ierror)
! open collective file
call mpi_file_open(mpi_comm_world, "testfile", mpi_mode_create +
mpi_mode_rdwr, mpi_info_null, fh, ierror)
! get size of datatype to calculate position in file
call mpi_type_size(dt, sz, ierror)
fs = n * sz
fp = myrank * m * sz
if (myrank == 0) then
write(*,*) sz
end if
! search position for each process
call mpi_file_seek(fh, fp, mpi_seek_set, ierror)
! write personal part
call mpi_file_write(fh, w, m, dt, stat, ierror)
! synchronize the file, so the master process can start reading
call mpi_file_sync(fh, ierror)
call mpi_barrier(mpi_comm_world, ierror)
if (myrank == 0) then
! inquire the file size and compare with the previously calculated
call mpi_file_get_size(fh, fs2, ierror)
write(*,*) "file size: ", fs2, "(", fs, ")"
! create space for read data
deallocate(w)
allocate(w(n))
! initialize all fields with -1
do i = 1, n
w(i) % r = -1
w(i) % i = -1
end do
! search the beginning
call mpi_file_seek(fh, int(0,kind=mpi_offset_kind), mpi_seek_set,
ierror)
! read all data
call mpi_file_read(fh, w, n, dt, stat, ierror)
write(*,*) ierror
write(*,*) stat
write(*,*)
! compare
correct = .true.
do i = 1, n
correct = correct .and. (v(i) % r == w(i) % r)
correct = correct .and. (v(i) % i == w(i) % i)
write(*,*) i
write(*,*) v(i) % r
write(*,*) w(i) % r
write(*,*) v(i) % i
write(*,*) w(i) % i
write(*,*)
end do
write(*,*) "original and read data are the same: ", correct
deallocate(v)
end if
deallocate(w)
! close file
call mpi_file_close(fh, ierror)
! finish
call mpi_finalize(ierror)
end program