4

我开始使用 MPI-IO 并尝试编写一个非常简单的示例来说明我想用它做的事情;然而,即使它是一个简单的代码,并且我从在这里和那里阅读的示例中获得了一些灵感,我还是遇到了一个我不理解的分段错误。

这段代码的逻辑非常简单:每个线程将处理一个本地数组,它是我要编写的全局数组的一部分。我使用它创建了一个子数组类型MPI_Type_Create_Subarray。然后我只是打开文件,设置视图并尝试写入数据。我在MPI_File_Write_All.

这是代码:

program test
  implicit none

  include "mpif.h"

  integer :: myrank, nproc, fhandle, ierr
  integer :: xpos, ypos
  integer, parameter :: loc_x=10, loc_y=10
  integer :: loc_dim
  integer :: nx=2, ny=2
  real(8), dimension(loc_x, loc_y) :: data
  integer :: written_arr
  integer, dimension(2) :: wa_size, wa_subsize, wa_start

  call MPI_Init(ierr)
  call MPI_Comm_Rank(MPI_COMM_WORLD, myrank, ierr)
  call MPI_Comm_Size(MPI_COMM_WORLD, nproc, ierr)

  xpos = mod(myrank, nx)
  ypos = mod(myrank/nx, ny)

  data = myrank

  loc_dim    = loc_x*loc_y
  wa_size    = (/ nx*loc_x, ny*loc_y /)
  wa_subsize = (/ loc_x, loc_y /)
  wa_start   = (/ xpos, ypos /)*wa_subsize
  call MPI_Type_Create_Subarray(2, wa_size, wa_subsize, wa_start &
       , MPI_ORDER_FORTRAN, MPI_DOUBLE_PRECISION, written_arr, ierr)
  call MPI_Type_Commit(written_arr, ierr)

  call MPI_File_Open(MPI_COMM_WORLD, "file.dat" &
       & , MPI_MODE_WRONLY + MPI_MODE_CREATE, MPI_INFO_NULL, fhandle, ierr)
  call MPI_File_Set_View(fhandle, 0, MPI_DOUBLE_PRECISION, written_arr &
       , "native", MPI_INFO_NULL, ierr)
  call MPI_File_Write_All(fhandle, data, loc_dim, MPI_DOUBLE_PRECISION &
       , MPI_INFO_NULL, ierr)
  call MPI_File_Close(fhandle, ierr)

  call MPI_Finalize(ierr)

end program test

任何帮助将不胜感激!

4

1 回答 1

7

错误输出参数之前的最后一个MPI_FILE_WRITE_ALL参数是 MPI 状态对象,而不是 MPI 信息对象。因此,拨打电话MPI_INFO_NULL是错误的。如果您对写操作的状态不感兴趣,那么您应该通过MPI_STATUS_IGNOREMPI_INFO_NULL由于两个常量的定义方式的特殊性,在某些 MPI 实现中调用 with可能会起作用,但在其他实现中会失败。

例如,在 Open MPIMPI_INFO_NULL中声明为:

parameter (MPI_INFO_NULL=0)

当传递而不是MPI_STATUS_IGNORE它时,会导致调用 C 实现,MPI_File_write_all其状态参数指向一个常量(只读)内存位置,该内存位置保存的值MPI_INFO_NULL(Fortran 如何实现按地址传递常量)。当 C 函数即将完成时,它会尝试填充状态对象,从而导致尝试写入常量内存并最终导致分段错误。


在编写新的 Fortran 程序时,建议不要使用非常旧的mpif.h接口,因为它不提供任何错误检查。而是应该使用该mpi模块,甚至mpi_f08在更多 MPI 实现符合 MPI-3.0 时使用。因此,您的程序的开头应如下所示:

program test
   use mpi
   implicit none
   ...
end program test

一旦使用mpi模块而不是mpif.h,编译器就能够对某些 MPI 调用(包括 )执行参数类型检查MPI_FILE_SET_VIEW,并发现错误:

test.f90(34): error #6285: There is no matching specific subroutine for this generic subroutine call.   [MPI_FILE_SET_VIEW]
  call MPI_File_Set_View(fhandle, 0, MPI_DOUBLE_PRECISION, written_arr &
-------^
compilation aborted for test.f90 (code 1)

原因是 的第二个参数MPI_FILE_SET_VIEW是 type INTEGER(KIND=MPI_OFFSET_KIND),在大多数现代平台上是 64 位的。常量0只是类型INTEGER,因此在大多数平台上都是 32 位的。发生的情况是,mpif.h编译器将指针传递给INTEGER值为 的常量0,但子例程将其解释为指向更大整数的指针,并将相邻值解释为常量值的一部分。因此,您作为文件内的偏移量传递的零最终成为非零值。

将调用0中的替换为或声明一个类型的常量和一个零值,然后传递它。MPI_FILE_SET_VIEW0_MPI_OFFSET_KINDINTEGER(KIND=MPI_OFFSET_KIND)

call MPI_File_Set_View(fhandle, 0_MPI_OFFSET_KIND, MPI_DOUBLE_PRECISION, ...

或者

integer(kind=MPI_OFFSET_KIND), parameter :: zero_off = 0
...
call MPI_File_Set_View(fhandle, zero_off, MPI_DOUBLE_PRECISION, ...

这两种方法都会生成一个大小为 3200 字节的输出文件(如预期的那样)。

于 2013-07-29T16:06:56.563 回答