MPI-IO中mpi_file_write_all异常排查:替换mpi_file_write后结果错误
MPI-IO集体写(MPI_File_write_all)数据顺序错误问题排查
你遇到的这个问题其实戳中了MPI-IO里独立写和集体写最核心的差异点——两者对文件视图的要求完全不一样,尤其是当你用到自定义索引类型的时候,集体写的规则要严格得多。
一、MPI_File_write 和 MPI_File_write_all 的核心区别
- 独立写(MPI_File_write):每个进程各干各的,不需要和其他进程同步,每个进程的文件视图只作用于自身的读写操作,系统会自行处理并发写入的冲突(只要你设计的偏移不重叠,就不会出问题)。你之前用独立写正常,就是因为每个进程自己定义了专属的位移规则,各自写自己的区域,互不干扰。
- 集体写(MPI_File_write_all):所有参与的进程必须同时调用这个函数,而且所有进程的文件视图定义必须全局一致。MPI会把集体写当成一个整体操作,基于统一的视图逻辑来分配每个进程的写入位置——如果每个进程的视图规则(比如你这里的自定义索引类型的位移)不一样,就会导致数据写入的重叠和顺序混乱,也就是你看到的
od -f结果错乱的情况。
二、你的代码问题出在哪?
看你的CREATE_TYPE子程序,每个进程的DISPLACEMENTS数组都是完全不同的:进程0的位移是[0,5,2,3],进程1是[4,1,6,7],以此类推。这种“各玩各的”位移规则在独立写时没问题,但集体写时,MPI会默认所有进程的视图是统一的,这就相当于每个进程都按照自己的规则往同一个逻辑视图里写,结果自然就乱了。
三、修改方案:适配集体写的规则
要让MPI_File_write_all正常工作,你需要调整自定义数据类型的设计,让每个进程的文件视图对应到文件的全局逻辑结构。这里给你一种适配原有需求的修改版本:
修改后的完整代码
PROGRAM INDEXED USE MPI IMPLICIT NONE REAL :: A(4) INTEGER :: INDEXTYPE,FH,IERR,L,N INTEGER(KIND=MPI_OFFSET_KIND) :: OFFSET CHARACTER(LEN=MPI_MAX_LIBRARY_VERSION_STRING) :: VERSION INTEGER :: MY_RANK, NUM_PROCS N=4 A(1)=1.0 A(2)=2.0 A(3)=3.0 A(4)=4.0 CALL MPI_INIT(IERR) CALL MPI_COMM_RANK(MPI_COMM_WORLD, MY_RANK, IERR) CALL MPI_COMM_SIZE(MPI_COMM_WORLD, NUM_PROCS, IERR) CALL MPI_GET_LIBRARY_VERSION(VERSION,L,IERR) IF(MY_RANK == 0) WRITE(*,*)TRIM(VERSION) ! 创建全局一致的索引类型,每个进程的位移对应全局文件位置 CALL CREATE_TYPE(INDEXTYPE,N) CALL MPI_FILE_OPEN(MPI_COMM_WORLD, "TEST", & MPI_MODE_RDWR+MPI_MODE_CREATE+MPI_MODE_DELETE_ON_CLOSE, MPI_INFO_NULL,FH,IERR) CALL MPI_CHECK_CALL(IERR) ! 集体写时,视图起始偏移设为0,自定义类型的位移直接对应全局文件的REAL单位位置 OFFSET=0 CALL MPI_FILE_SET_VIEW(FH, OFFSET,MPI_REAL, & INDEXTYPE,'NATIVE', & MPI_INFO_NULL, IERR) CALL MPI_CHECK_CALL(IERR) ! 切换为集体写调用 CALL MPI_FILE_WRITE_ALL(FH,A,N,MPI_REAL, & MPI_STATUS_IGNORE,IERR) CALL MPI_CHECK_CALL(IERR) CALL MPI_FILE_CLOSE(FH,IERR) CALL MPI_CHECK_CALL(IERR) CALL MPI_TYPE_FREE(INDEXTYPE, IERR) ! 记得释放已提交的MPI类型 CALL MPI_FINALIZE(IERR) END PROGRAM INDEXED SUBROUTINE CREATE_TYPE(DATARES_TYPE,N) USE MPI IMPLICIT NONE INTEGER, INTENT(OUT) :: DATARES_TYPE INTEGER, INTENT(IN) :: N INTEGER :: IERR, MY_RANK INTEGER, ALLOCATABLE :: BLOCKLENS(:), DISPLACEMENTS(:) ALLOCATE(BLOCKLENS(N)) ALLOCATE(DISPLACEMENTS(N)) BLOCKLENS = 1 CALL MPI_COMM_RANK(MPI_COMM_WORLD, MY_RANK, IERR) ! 关键修改:每个进程的位移是全局文件中的位置(以MPI_REAL为单位) ! 所有进程都知道完整的类型结构,只是每个进程只写入自己对应的那部分位移 SELECT CASE(MY_RANK) CASE(0) DISPLACEMENTS = [0,5,2,3] CASE(1) DISPLACEMENTS = [4,1,6,7] CASE(2) DISPLACEMENTS = [8,9,14,11] CASE(3) DISPLACEMENTS = [12,13,10,15] END SELECT CALL MPI_TYPE_INDEXED(N, BLOCKLENS, DISPLACEMENTS, & MPI_REAL, DATARES_TYPE, IERR) CALL MPI_CHECK_CALL(IERR) CALL MPI_TYPE_COMMIT(DATARES_TYPE, IERR) CALL MPI_CHECK_CALL(IERR) DEALLOCATE(BLOCKLENS) DEALLOCATE(DISPLACEMENTS) END SUBROUTINE SUBROUTINE MPI_CHECK_CALL(IERR) USE MPI IMPLICIT NONE INTEGER, INTENT(IN) :: IERR INTEGER :: NERR, RESULTLEN CHARACTER(LEN=MPI_MAX_ERROR_STRING) :: SERR IF(IERR /= MPI_SUCCESS) THEN CALL MPI_ERROR_STRING(IERR,SERR,RESULTLEN,NERR) WRITE(*,*)TRIM(SERR) CALL BACKTRACE END IF END SUBROUTINE
四、额外注意事项
- OpenMPI 3.0对集体IO的支持是稳定的,但要确保所有进程的
MPI_File_set_view参数(数据类型、数据表示、起始偏移)逻辑一致——即使每个进程写入的位置不同,也要基于同一个全局视图规则。 - 集体IO的优势是性能(尤其是大规模进程时),但代价是严格的同步和视图一致性要求,这是和独立IO最本质的区别。
内容的提问来源于stack exchange,提问作者Ray
相关产品推荐
相关产品推荐

