用 MPI IO 写几个分布式数组
Write several distributed arrays with MPI IO
我正在重写一个在一个方向上使用 MPI 并行化的数值模拟代码。
到目前为止,包含数据的数组由主 MPI 进程保存,这意味着将数据从所有 MPI 进程传输到一个进程并分配巨大的数组来存储整个过程。它不是很有效也不优雅,并且对于大分辨率来说是个问题。
因此,我尝试使用 MPI-IO 直接从分布式阵列写入文件。我的限制之一是写入的文件需要遵守 fortran "unformatted" 格式(即每个字段前后的 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
当 !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
cmp 不生成输出,因此文件相同。
但是,如果我取消注释这些行,则生成的文件(mpi.dat 和 fort.10)是不同的。我确定问题出在我定义用于将数据写入文件正确位置的偏移量的方式,但我不知道如何向 saveMPI 的第二次调用指示初始位置应该是结束的文件。我错过了什么?
只有对 saveMPI
的第一次调用按预期工作。第二次打电话后,一切都变得一团糟。以下是正在发生的事情的一些迹象:
MPI_File_set_view
将独立文件指针和共享文件指针重置为零。有关详细信息,请参阅 MPI_File_set_view。因此,当您在 saveMPI
. 中调用 MPI_File_set_view
时,实际上是用 w
数据覆盖了 v
数据
- 和
MPI_File_write
,数据被写入当前视图指定的文件的那些部分。这意味着您将大小信息添加到文件中的方式与之前为 v
. 设置的视图并不真正兼容
- 用
MPI_SEEK_CUR
调用 MPI_File_seek
设置相对于单个指针当前位置的位置。所以,对于第二次调用,是相对于进程的个体指针0
我没有那么多使用并行 IO,所以我无能为力,除非我进入文档,但我没有时间。我可以给出的提示是:
- 向
saveMPI
添加一个附加参数,该参数将包含要写入的数据的绝对位移;这可以是一个 [in out]
参数。对于第一次调用,它将为零,对于后续调用,它将是已写入文件的所有数据的大小,包括大小信息。可以在saveMPI
. 更新
- 在写入大小信息之前(通过进程 0)调用
MPI_File_set_view
将视图重置为最初由 MPI_File_open
给出的线性字节流。这可以通过在调用 MPI_File_set_view
时将 etype
和 filetype
都设置为 MPI_BYTE
来完成。查看 doc of MPI_File_open
了解更多信息。然后,您必须在 saveMPI
. 中调用 MPI_File_set_view
您的 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
并称呼为:
disp = 0
call saveMPI(v_loc, (i_loc)*(jmax+2)*(kmax+2), disp)
call saveMPI(w_loc, (i_loc)*(jmax+2)*(kmax+2), disp)
最后,请确保在两次调用之间删除文件,因为您正在使用 MPI_MODE_APPEND
。
我正在重写一个在一个方向上使用 MPI 并行化的数值模拟代码。 到目前为止,包含数据的数组由主 MPI 进程保存,这意味着将数据从所有 MPI 进程传输到一个进程并分配巨大的数组来存储整个过程。它不是很有效也不优雅,并且对于大分辨率来说是个问题。
因此,我尝试使用 MPI-IO 直接从分布式阵列写入文件。我的限制之一是写入的文件需要遵守 fortran "unformatted" 格式(即每个字段前后的 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
当 !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
cmp 不生成输出,因此文件相同。 但是,如果我取消注释这些行,则生成的文件(mpi.dat 和 fort.10)是不同的。我确定问题出在我定义用于将数据写入文件正确位置的偏移量的方式,但我不知道如何向 saveMPI 的第二次调用指示初始位置应该是结束的文件。我错过了什么?
只有对 saveMPI
的第一次调用按预期工作。第二次打电话后,一切都变得一团糟。以下是正在发生的事情的一些迹象:
MPI_File_set_view
将独立文件指针和共享文件指针重置为零。有关详细信息,请参阅 MPI_File_set_view。因此,当您在saveMPI
. 中调用 - 和
MPI_File_write
,数据被写入当前视图指定的文件的那些部分。这意味着您将大小信息添加到文件中的方式与之前为v
. 设置的视图并不真正兼容
- 用
MPI_SEEK_CUR
调用MPI_File_seek
设置相对于单个指针当前位置的位置。所以,对于第二次调用,是相对于进程的个体指针0
MPI_File_set_view
时,实际上是用 w
数据覆盖了 v
数据
我没有那么多使用并行 IO,所以我无能为力,除非我进入文档,但我没有时间。我可以给出的提示是:
- 向
saveMPI
添加一个附加参数,该参数将包含要写入的数据的绝对位移;这可以是一个[in out]
参数。对于第一次调用,它将为零,对于后续调用,它将是已写入文件的所有数据的大小,包括大小信息。可以在saveMPI
. 更新
- 在写入大小信息之前(通过进程 0)调用
MPI_File_set_view
将视图重置为最初由MPI_File_open
给出的线性字节流。这可以通过在调用MPI_File_set_view
时将etype
和filetype
都设置为MPI_BYTE
来完成。查看 doc ofMPI_File_open
了解更多信息。然后,您必须在saveMPI
. 中调用
MPI_File_set_view
您的 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
并称呼为:
disp = 0
call saveMPI(v_loc, (i_loc)*(jmax+2)*(kmax+2), disp)
call saveMPI(w_loc, (i_loc)*(jmax+2)*(kmax+2), disp)
最后,请确保在两次调用之间删除文件,因为您正在使用 MPI_MODE_APPEND
。