Fortran 无格式输出,每个 MPI 进程写入数组的一部分

Fortran unformatted output with each MPI process writing part of an array

在我的并行程序中,有一个很大的矩阵。每个进程计算并存储它的一部分。然后程序通过让每个进程以正确的顺序写入自己的矩阵部分来将矩阵写入文件。输出文件为 "unformatted" 格式。但是当我试图以串行代码读取文件时(我分配了正确大小的大矩阵),我得到了一个我不明白的错误。

我的问题是:在MPI程序中,如何获取二进制文件作为由不同进程存储的大矩阵的串行版本输出?

这是我的尝试:

    if(ThisProcs == RootProcs) then
        open(unit = file_restart%unit, file = file_restart%file, form = 'unformatted')
        write(file_restart%unit)psi
        close(file_restart%unit)
    endif
#ifdef USEMPI
    call mpi_barrier(mpi_comm_world,MPIerr)
#endif
    do i = 1, NProcs - 1
        if(ThisProcs == i) then
            open(unit = file_restart%unit, file = file_restart%file, form = 'unformatted', status = 'old', position = 'append')
            write(file_restart%unit)psi
            close(file_restart%unit)
        endif
#ifdef USEMPI
        call mpi_barrier(mpi_comm_world,MPIerr)
#endif
    enddo

Psi是大矩阵,分配为:

Psi(N_lattice_points, NPsiStart:NPsiEnd)

但是当我尝试以串行代码加载文件时:

open(2,file=File1,form="unformatted")
read(2)psi

forrtl: severe (67): input statement requires too much data, unit 2 (I am using MSVS 2012+intel fortran 2013)

如何修复并行部分以使串行代码可读二进制文件?当然可以在MPI程序中将它们组合成一个大矩阵,但是有没有更简单的方法呢?

编辑 1

两位的回答真不错。我将使用 access = "stream" 来解决我的问题。我只是想我可以使用 inquire 来检查文件是 "sequential" 还是 "stream".

记录文件中的 Fortran 未格式化顺序写入不完全是原始数据。每次写入都将在处理器相关形式的记录前后有数据。读取的大小不能超过写入的记录大小。这意味着如果 psi 是分两次写的,你需要分两次读回来,你不能一次读进去。

也许最直接的选择是使用 stream 访问权限而不是 sequential。流文件(通常)按字节索引,不包含记录开始和结束信息。使用这种访​​问方法,您可以拆分写入,但同时读取所有内容。流访问是 Fortran 2003 的一项功能。

如果您坚持使用顺序访问,您将需要知道有多少 MPI 等级写入文件并循环遍历适当大小的记录以读取写入的数据。您可以让用户指定等级数或将其存储为文件中的第一条记录并首先读取它以确定如何读取其余数据。

这不是 MPI 特有的问题,但也会发生在串行程序中,该程序采用零碎写出块的相同方法。

忽略每个进程的打开和关闭,查看整体连接和传输语句。您的连接是使用顺序访问的未格式化文件。它是未格式化的,因为您明确要求它,并且是顺序的,因为您没有要求任何其他东西。

顺序文件访问基于记录。您的每个写入语句都会传输出一条由矩阵块组成的记录。相反,您的输入语句尝试从单个记录中读取。

您的问题是,当您尝试从文件的第一条记录读取整个矩阵时,该记录不包含整个矩阵。它不包含任何正确数量的数据。最终结果:"input statement requires too much data".

因此,您需要根据相同的记录结构读入数据,或者离开记录文件。

后者简单,使用流访问

open(unit = file_restart%unit, file = file_restart%file,  &
     form = 'unformatted', access='stream')

或者,使用类似的循环结构读取:

do i=1, NPROCS
  ! read statement with a slice
end do

这当然需要理解正确的切片。

或者,可以考虑使用 MPI-IO 进行输出,这与使用流输出非常相似。使用流访问读回。您可以在 SO 的其他地方找到有关此概念的信息。

如果您正在编写 MPI,为什么不编写 MPI-IO?每个进程都会调用 MPI_File_set_view 设置文件的子数组视图,然后每个进程可以用 MPI_FILE_WRITE_ALL 集体写入数据。这种方法很可能在大型机器上很好地扩展(尽管你的方法可以很好地扩展到哦,也许 100 个处理器。)