部分处理器无数据时能否使用MPI_FILE_WRITE_ALL?
问题
我正在修改同事的Fortran90子例程,要把多处理器高保真流体模拟的3D笛卡尔数据子集写入单个二进制文件,用于可视化导出切片或小型连续3D子集。通常并非所有处理器都会与指定切片相交,请问部分处理器无数据可写时,能否使用MPI_FILE_WRITE_ALL?还是必须仅使用非集体子例程?
此前导出完整3D数据的子例程依赖MPI_TYPE_CREATE_SUBARRAY、MPI_FILE_SET_VIEW、MPI_TYPE_COMMIT和MPI_FILE_WRITE_ALL。我不太清楚哪些是集体操作,哪些需要替换为本地例程。刚接触MPI,若问题基础还请见谅,也欢迎提供可靠文档指引。
编辑:经过一番摸索,我重新梳理了问题,希望代码段实现如下功能:
! Relevant variables and their types real(WP), pointer :: r1_data(:) integer :: ierr, r1_type, fh, offset integer :: sizes(3), subsizes(3), starts(3), status(MPI_STATUS_SIZE) integer :: my_data_count, data_count integer :: i_start, j_start, k_start integer :: i_end, j_end, k_end integer :: index real(WP), allocatable :: my_data(:,:,:) !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ! Initialization code, including correctly ! populating 'starts', 'sizes', 'subsizes' and ! offset takes place here. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! call MPI_TYPE_CREATE_SUBARRAY(3, sizes, subsizes, starts, MPI_ORDER_FORTRAN,MPI_REAL_WP,r1_type,ierr) call MPI_TYPE_COMMIT(r1_type, ierr) !-- populate the data buffer index = 0 do k=k_start,k_end do j=j_start,j_end do i=i_start,i_end index = index + 1 r1_data(index) = my_data(i,j,k) enddo enddo enddo call MPI_FILE_SET_VIEW(fh,offset,MPI_REAL_WP,r1_type,'native',MPI_INFO_NULL,ierr) call MPI_FILE_WRITE_ALL(fh,r1_data,my_data_count,MPI_REAL_WP,status,ierr)
当所有处理器都有子数组部分可写时这段代码运行正常,但当某处理器的本地子数组大小为0(subsizes的一个或多个条目为0)时,无法调用MPI_TYPE_CREATE_SUBARRAY,导致r1_type未赋值。我希望尽量使用高效的集体写入操作,但这并非硬性要求。
回答
核心结论
可以继续使用集体操作,但需要对无数据的处理器做特殊处理,避免创建无效的子数组类型。
具体处理方案
提前判断本地数据量
在调用MPI_TYPE_CREATE_SUBARRAY前,先计算本地子数组的总元素数:my_data_count = product(subsizes)。如果my_data_count == 0,说明该处理器无数据可写,跳过子数组类型的创建和缓冲区填充步骤,但仍需参与所有集体操作(MPI集体操作要求通信域内所有进程都调用)。集体操作的兼容处理
MPI_FILE_SET_VIEW:无数据的进程可以传入MPI_DATATYPE_NULL作为类型参数,或者保持与其他进程一致的视图配置,后续写入0个元素即可。MPI_FILE_WRITE_ALL:无数据的进程直接传入count=0,缓冲区可传空指针或任意有效地址(不会实际执行写入),调用会立即返回且不破坏集体操作一致性。
代码调整示例
修改后的关键代码片段如下:my_data_count = product(subsizes) if (my_data_count > 0) then call MPI_TYPE_CREATE_SUBARRAY(3, sizes, subsizes, starts, MPI_ORDER_FORTRAN, MPI_REAL_WP, r1_type, ierr) call MPI_TYPE_COMMIT(r1_type, ierr) ! 填充数据缓冲区 index = 0 do k=k_start,k_end do j=j_start,j_end do i=i_start,i_end index = index + 1 r1_data(index) = my_data(i,j,k) enddo enddo enddo else r1_type = MPI_DATATYPE_NULL ! 无需填充缓冲区 endif ! 所有进程统一调用文件视图设置 call MPI_FILE_SET_VIEW(fh, offset, MPI_REAL_WP, r1_type, 'native', MPI_INFO_NULL, ierr) ! 所有进程执行集体写入,无数据进程写入0个元素 call MPI_FILE_WRITE_ALL(fh, r1_data, my_data_count, MPI_REAL_WP, status, ierr) ! 仅释放已创建的自定义类型 if (my_data_count > 0) then call MPI_TYPE_FREE(r1_type, ierr) endif
MPI操作类型说明
- 集体操作:
MPI_FILE_SET_VIEW、MPI_FILE_WRITE_ALL、MPI_TYPE_COMMIT。这类操作要求通信域内所有进程同步调用,哪怕无数据的进程也不能跳过,否则会引发死锁或程序崩溃。 - 本地操作:
MPI_TYPE_CREATE_SUBARRAY、MPI_TYPE_FREE。这类操作由单个进程独立执行,无数据进程可选择跳过。
关键规则参考
MPI标准明确:集体IO操作允许进程写入0个元素,此时操作无实际IO行为;而MPI_TYPE_CREATE_SUBARRAY不接受维度为0的subsizes参数,因此必须提前判断并跳过无效的类型创建。
内容的提问来源于stack exchange,提问作者StillUsesFORTRAN

