mer*_*ruz 5 fortran mpi fortran90 mpi-io
我正在重写一个使用MPI在一个方向上并行化的数值模拟代码.到目前为止,包含数据的数组由主MPI进程保存,这意味着将数据从所有MPI进程传输到一个并分配大型数组来存储整个数据.它不是非常有效也不是优雅,并且是大分辨率的问题.
因此,我尝试使用MPI-IO直接从分布式阵列中写入文件.我的一个约束是写入文件需要遵守fortran"未格式化"格式(即每个字段之前和之后的4个字节整数,表示其大小).
我写了一个简单的测试程序,当我只将一个分布式数组写入文件时,它就可以工作.但是,当我写几个数组时,文件的总大小是错误的,当与同等的fortran'unformatted'文件进行比较时,文件是不同的.
以下是示例代码:
module arrays_dim
   implicit none
   INTEGER,        PARAMETER :: dp   = kind(0.d0) 
   integer,        parameter :: imax = 500 
   integer,        parameter :: jmax = 50 
   integer,        parameter :: kmax = 10 
end module arrays_dim
module mpi_vars
   use mpi 
   implicit none
   integer, save          :: ierr, myID, numprocs
   integer, save          :: i_start, i_end, i_mean, i_loc
   integer, save          :: subArray, fileH
   integer(MPI_OFFSET_KIND), save   :: offset, currPos
end module mpi_vars
program test
   use mpi 
   use arrays_dim
   use mpi_vars
   real(dp), dimension(0:imax,0:jmax+1,0:kmax+1) :: v, w
   real(dp), dimension(:,:,:), allocatable       :: v_loc, w_loc
   integer                                       :: i, j, k
   call MPI_INIT(ierr) 
   call MPI_COMM_RANK(MPI_COMM_WORLD, myID, ierr) 
   call MPI_COMM_SIZE(MPI_COMM_WORLD, numprocs, ierr) 
   i_mean = (imax+1)/numprocs
   i_start = myID*i_mean
   i_end   = i_start+i_mean-1
   if(i_mean*numprocs<imax+1) then 
    if(myID == numprocs-1) i_end = imax
   endif
   i_loc = i_end - i_start + 1
   allocate(v_loc(i_start:i_end,0:jmax+1,0:kmax+1))
   allocate(w_loc(i_start:i_end,0:jmax+1,0:kmax+1))
   print*, 'I am:', myID, i_start, i_end, i_loc
   do k=0,kmax+1
      do j=0,jmax+1
         do i=0,imax
            v(i,j,k) = i+j+k
            w(i,j,k) = i*j*k
         enddo
      enddo
   enddo
   if(myID==0) then 
       open(10,form='unformatted')
       write(10) v
       !write(10) w
       close(10)
   endif
   do k=0,kmax+1
      do j=0,jmax+1
         do i=i_start,i_end
            v_loc(i,j,k) = i+j+k
            w_loc(i,j,k) = i*j*k
         enddo
      enddo
   enddo
   call MPI_Type_create_subarray (3, [imax+1, jmax+2, kmax+2], [i_loc, jmax+2, kmax+2], &
                                     [i_start, 0, 0], &
                                    MPI_ORDER_FORTRAN, MPI_DOUBLE_PRECISION, subArray,  ierr)
   call MPI_Type_commit(subArray, ierr)
   call MPI_File_open(MPI_COMM_WORLD, 'mpi.dat',         &
                     MPI_MODE_WRONLY + MPI_MODE_CREATE + MPI_MODE_APPEND, &
                     MPI_INFO_NULL, fileH, ierr )   
   call saveMPI(v_loc, (i_loc)*(jmax+2)*(kmax+2))
   !call saveMPI(w_loc, (i_loc)*(jmax+2)*(kmax+2))
   call MPI_File_close(fileH, ierr)      
   deallocate(v_loc,w_loc)
   call MPI_FINALIZE(ierr) 
end program test
!
subroutine saveMPI(array, n)
   use mpi
   use arrays_dim
   use mpi_vars
   implicit none
   real(dp), dimension(n) :: array
   integer                   :: n
   offset = (imax+1)*(jmax+2)*(kmax+2)*8
   if(myID==0) then
     call MPI_File_seek(fileH, int(0,MPI_OFFSET_KIND), MPI_SEEK_CUR, ierr)
     call MPI_File_write(fileH, [(imax+1)*(jmax+2)*(kmax+2)*8], 1, MPI_INTEGER, MPI_STATUS_IGNORE, ierr)
     call MPI_File_seek(fileH, offset, MPI_SEEK_CUR, ierr)
     call MPI_File_write(fileH, [(imax+1)*(jmax+2)*(kmax+2)*8], 1, MPI_INTEGER, MPI_STATUS_IGNORE, ierr)
   endif 
   call MPI_File_set_view(fileH, int(4,MPI_OFFSET_KIND), MPI_DOUBLE_PRECISION, subArray, 'native', MPI_INFO_NULL, ierr)
   call MPI_File_write_all(fileH, array, (i_loc)*(jmax+2)*(kmax+2), MPI_DOUBLE_PRECISION, MPI_STATUS_IGNORE, ierr)  
end subroutine saveMPI
Run Code Online (Sandbox Code Playgroud)
当行!write(10) w和!call saveMPI(w_loc, (i_loc)*(jmax+2)*(kmax+2))被注释(即我只写v阵列)时,代码工作正常:
mpif90.openmpi -O3 -o prog main.f90
mpirun.openmpi -np 4 ./prog
cmp mpi.dat fort.10
Run Code Online (Sandbox Code Playgroud)
cmp不生成输出,因此文件是相同的.但是,如果我取消注释这些行,则生成的文件(mpi.dat和fort.10)是不同的.我确定问题在于我定义用于在文件的正确位置写入数据的偏移量的方式,但我不知道如何向saveMPI的第二次调用指示初始位置应该是结束的文件.我错过了什么?
只有第一次调用才能saveMPI按您的预期工作。从第二次通话开始,一切都变得一团糟。以下是正在发生的事情的一些迹象:
MPI_File_set_view将独立文件指针和共享文件指针重置为零。有关详细信息,请参阅MPI_File_set_view 。所以当你调用时实际上是v用数据覆盖数据。wMPI_File_set_viewsaveMPIMPI_File_write,数据将写入当前视图指定的文件部分。这意味着您将大小信息添加到文件中的方式与之前为v.MPI_File_seekwithMPI_SEEK_CUR设置相对于单个指针当前位置的位置。所以,对于第二次调用,它是相对于进程的个体指针0 我不太使用并行 IO,所以我无法提供更多帮助,除非我进入文档,但我没有时间。我可以给出的提示是:
saveMPI包含要写入的数据的绝对位移;这可以是一个[in out]参数。对于第一次调用,它将为零,对于后续调用,它将是已写入文件的所有数据的大小,包括大小信息。它可以在 中更新saveMPI。MPI_File_set_view将视图重置为线性字节流,如 最初给出的那样MPI_File_open。这可以通过在调用中设置etype和filetype来完成。查看文档以获取更多信息。然后您将不得不调用in 。MPI_BYTEMPI_File_set_viewMPI_File_openMPI_File_set_viewsaveMPI你的saveMPI子程序可能看起来像
subroutine saveMPI(array, n, disp)
    use mpi
    use arrays_dim
    use mpi_vars
    implicit none
    real(dp), dimension(n) :: array
    integer                   :: n, disp
    offset = (imax+1)*(jmax+2)*(kmax+2)*8
    call MPI_File_set_view(fileH, int(disp,MPI_OFFSET_KIND), MPI_BYTE, MPI_BYTE, 'native', MPI_INFO_NULL, ierr)
    if(myID==0) then
        call MPI_File_seek(fileH, int(0,MPI_OFFSET_KIND), MPI_SEEK_END, ierr)
        call MPI_File_write(fileH, [(imax+1)*(jmax+2)*(kmax+2)*8], 1, MPI_INTEGER, MPI_STATUS_IGNORE, ierr)
        call MPI_File_seek(fileH, int(offset,MPI_OFFSET_KIND), MPI_SEEK_CUR, ierr)
        call MPI_File_write(fileH, [(imax+1)*(jmax+2)*(kmax+2)*8], 1, MPI_INTEGER, MPI_STATUS_IGNORE, ierr)
    endif
    call MPI_File_set_view(fileH, int(disp+4,MPI_OFFSET_KIND), MPI_DOUBLE_PRECISION, subArray, 'native', MPI_INFO_NULL, ierr)
    call MPI_File_write_all(fileH, array, (i_loc)*(jmax+2)*(kmax+2), MPI_DOUBLE_PRECISION, MPI_STATUS_IGNORE, ierr)
    disp = disp+offset+8
end subroutine saveMPI
Run Code Online (Sandbox Code Playgroud)
并这样称呼:
disp = 0
call saveMPI(v_loc, (i_loc)*(jmax+2)*(kmax+2), disp)
call saveMPI(w_loc, (i_loc)*(jmax+2)*(kmax+2), disp)
Run Code Online (Sandbox Code Playgroud)
最后,请确保在两次调用之间删除该文件,因为您正在使用MPI_MODE_APPEND.