MPI_Waitall报错“无效请求”:流求解器并行化问题求助
问题:MPI非阻塞通信Waitall报错:无效请求句柄
问题背景
在博士研究中为流求解器实现并行化,继承了一个预定义子域间传输数据的子程序,采用MPI_Isend/MPI_Irecv非阻塞通信,随后调用MPI_Waitall等待通信完成,但程序终止并报错。
问题代码
! ----------------------------------------------------------- ! Definition of instant send/receive passings with barrier at the end ! ----------------------------------------------------------- spos=1 ! Position of the first element to send within send array do i=1,isize ! loop over the number of exchanging segments if (nsendseg(i).ne.0) then ! choose only domains with something to send call MPI_ISend(send(spos),nsendseg(i),MPI_REAL8,i-1,1,MPI_COMM_WORLD,reqs(i),ierr) spos=spos+nsendseg(i) end if enddo rpos=1 do i=1,isize if (nrecvseg(i).ne.0) then call MPI_IRecv(recv(rpos),nrecvseg(i),MPI_REAL8,i-1,MPI_ANY_TAG,MPI_COMM_WORLD,reqs(i+sum(nsendseg)),ierr) rpos=rpos+nrecvseg(i) end if end do if (irank .eq. 0) print *, reqs call MPI_Waitall(sum(nsendseg)+sum(nrecvseg),reqs,MPI_STATUSES_IGNORE,ierr)
补充说明
sum(nsendseg)+sum(nrecvseg):nsendseg为各核心需发送的节点数数组,nrecvseg为接收节点数数组,每个核心仅与相关核心进行收发操作。
报错信息
Abort(336210451) on node 13 (rank 13 in comm 0): Fatal error in PMPI_Waitall: Request pending due to failure, error stack: PMPI_Waitall(352): MPI_Waitall(count=28734, req_array=0x18ac060, status_array=0x1) failed PMPI_Waitall(328): The supplied request in array element 2 was invalid (kind=0)
自我推测
怀疑reqs数组未正确接收通信句柄,reqs为默认整数数组,内容存在异常值。
解答
1. 核心问题定位
当前代码存在两个致命错误:
- 请求数组索引逻辑错误:发送循环中直接用
i作为reqs索引,当nsendseg(i)=0时会跳过对应位置,导致reqs部分元素未初始化;接收请求的索引计算混淆了数据元素总数和请求句柄个数,sum(nsendseg)是发送的节点总数,不是发送请求的数量,直接用它作为偏移会导致索引越界或覆盖无效位置。 - 请求句柄类型错误:MPI请求句柄不是普通整数,在Fortran中必须声明为
TYPE(MPI_Request)类型,用默认整数存储会导致内存访问错误,生成无效句柄。
2. MPI请求句柄的正确形式
在Fortran中,必须显式声明请求数组为MPI提供的类型:
TYPE(MPI_Request), ALLOCATABLE :: reqs(:)
不能用普通整数数组替代,否则会破坏MPI内部的句柄管理逻辑。
3. 代码修复步骤
步骤1:正确声明并分配请求数组
先统计实际需要的请求个数(发送请求数为nsendseg中非零元素的数量,接收请求数同理),再动态分配数组:
integer :: count_send, count_recv count_send = count(nsendseg /= 0) count_recv = count(nrecvseg /= 0) allocate(reqs(count_send + count_recv))
步骤2:修正请求数组的索引赋值
用独立计数器跟踪reqs的当前位置,避免循环变量直接作为索引:
! 发送部分 spos=1 integer :: req_idx req_idx = 1 do i=1,isize if (nsendseg(i).ne.0) then call MPI_ISend(send(spos), nsendseg(i), MPI_REAL8, i-1, 1, MPI_COMM_WORLD, reqs(req_idx), ierr) ! 增加错误检查 if (ierr /= MPI_SUCCESS) call MPI_Abort(MPI_COMM_WORLD, ierr, ierr) spos=spos+nsendseg(i) req_idx = req_idx + 1 end if enddo ! 接收部分 rpos=1 do i=1,isize if (nrecvseg(i).ne.0) then call MPI_IRecv(recv(rpos), nrecvseg(i), MPI_REAL8, i-1, 1, MPI_COMM_WORLD, reqs(req_idx), ierr) if (ierr /= MPI_SUCCESS) call MPI_Abort(MPI_COMM_WORLD, ierr, ierr) rpos=rpos+nrecvseg(i) req_idx = req_idx + 1 end if end do
步骤3:修正MPI_Waitall的参数
MPI_Waitall的第一个参数是请求句柄的个数,而非数据元素总数:
call MPI_Waitall(count_send + count_recv, reqs, MPI_STATUSES_IGNORE, ierr)
4. 额外建议
- 强制检查MPI调用返回值:每一次MPI调用后都检查
ierr,能提前定位通信初始化阶段的错误。 - 避免滥用
MPI_ANY_TAG:如果发送端固定使用tag=1,接收端直接指定tag=1更安全,防止接收无关消息导致逻辑混乱。 - 调试时聚焦关键指标:不要打印整个
reqs数组,改为打印count_send、count_recv和req_idx的最终值,确认请求数量匹配。
内容的提问来源于stack exchange,提问作者Subject303
相关产品推荐
相关产品推荐

