MPI 非阻塞发送/接收中的请求数组

Posted

技术标签:

【中文标题】MPI 非阻塞发送/接收中的请求数组【英文标题】:request array in MPI non-blocking send/recv 【发布时间】:2014-08-15 05:41:50 【问题描述】:

我正在尝试在 Fortran 中重现 this C 示例。到目前为止我的代码:

use mpi

implicit none
integer, parameter :: maxn = 8
integer, allocatable :: xlocal(:,:)

integer :: i, j, lsize, errcnt, toterr, buff

integer :: ierror, nproc, pid, root = 0, nreq = 0
integer, allocatable :: request(:), status(:,:)

call MPI_INIT(ierror)
call MPI_COMM_SIZE(MPI_COMM_WORLD, nproc, ierror)
call MPI_COMM_RANK(MPI_COMM_WORLD, pid, ierror)

if (mod(maxn, nproc) /= 0) then
    write(*,*) 'Array size (maxn) should be a multiple of the number of processes'
    call MPI_ABORT(MPI_COMM_WORLD, 1, ierror)
end if

lsize = maxn/nproc

allocate(xlocal(0:lsize+1, maxn))
allocate(request(nproc))
allocate(status(MPI_STATUS_SIZE,nproc))

xlocal(0,:) = -1
xlocal(1:lsize,:) = pid
xlocal(lsize+1,:) = -1

! send down unless on bottom
if (pid < nproc-1) then
    nreq = nreq + 1
    call MPI_ISEND(xlocal(lsize,:), maxn, MPI_INTEGER, &
                  pid+1, 0, MPI_COMM_WORLD, request(nreq), ierror)
    write(*,'(2(A,I1),A)') 'process ', pid, ' sent to process ', pid+1, ':'
    write(*,*) xlocal(lsize,:)
end if

if (pid > 0) then
    nreq = nreq + 1
    call MPI_IRECV(xlocal(0,:), maxn, MPI_INTEGER, &
                  pid-1, 0, MPI_COMM_WORLD, request(nreq), ierror)
    write(*,'(2(A,I1),A)') 'process ', pid, ' received from process ', pid-1, ':'
    write(*,*) xlocal(0,:)
end if

! send up unless on top
if (pid > 0) then
    nreq = nreq + 1
    call MPI_ISEND(xlocal(1,:), maxn, MPI_INTEGER, &
                  pid-1, 1, MPI_COMM_WORLD, request(nreq), ierror)
    write(*,'(2(A,I1),A)') 'process ', pid, ' sent to process ', pid-1, ':'
    write(*,*) xlocal(1,:)
end if

if (pid < nproc-1) then
    nreq = nreq + 1
    call MPI_IRECV(xlocal(lsize+1,:), maxn, MPI_INTEGER, &
                  pid+1, 1, MPI_COMM_WORLD, request(nreq), ierror)
    write(*,'(2(A,I1),A)') 'process ', pid, ' received from process ', pid+1, ':'
    write(*,*) xlocal(lsize+1,:)
end if

call MPI_WAITALL(nreq, request, status, ierror)

! check results
errcnt = 0
do i = 1, lsize
    do j = 1, maxn
        if (xlocal(i,j) /= pid) errcnt = errcnt + 1
    end do
end do
do j = 1, maxn
    if (xlocal(0,j) /= pid-1) errcnt = errcnt + 1
    if ((pid < nproc-1) .and. (xlocal(lsize+1,j) /= pid+1)) errcnt = errcnt + 1
end do

call MPI_REDUCE(errcnt, toterr, 1, MPI_INTEGER, MPI_SUM, 0, MPI_COMM_WORLD)

if (pid == root) then
    if (toterr == 0) then
        write(*,*) "no errors found"
    else
        write(*,*) "found ", toterr, " errors"
    end if
end if

deallocate(xlocal)
deallocate(request)
deallocate(status)

call MPI_FINALIZE(ierror)

但我遇到了分段错误,无法弄清楚原因。我有一种感觉是由于请求数组。有人可以解释在 Fortran 中使用请求数组的正确方法吗?我找到的任何参考资料都没有说明这一点。

提前谢谢

【问题讨论】:

【参考方案1】:

如果您还没有这样做,请考虑使用一些有助于调试的标志来编译您的程序,例如对于gfortran,您可以使用-O0 -g -fbounds-check(如果这没有帮助,您可以为版本>= 4.8 添加-fsanitize=address)。其他编译器也有类似的调试选项。

这样做并使用 2 个进程运行,您的程序会在 MPI_Reduce 行崩溃。如果您查看规范(例如OpenMPI 1.8),您会发现此子例程需要一个参数,即您忘记在末尾添加ierror 参数。

有点可悲的是,即使 mpi 模块中的子程序可以通过 use 关联访问,因此应该检查参数一致性以避免这些微不足道的错误,但并非所有子程序都必须在该模块中。我不知道您使用哪种 MPI 实现,但我检查了我的本地 MPICH 安装,它在模块中没有大多数子例程,因此它们不存在显式接口。我猜您处于类似的情况,但我猜其他实现可能会遭受类似的命运。您可以将其与缺少 MPI_Reduce 的函数原型的 C 头文件进行比较。我猜这是因为最初大多数实现只有一个 Fortran 77 接口。

一些最终的 cmets:注意不要只是复制粘贴 C 代码。您传递的数组不是连续的,并且会导致将临时副本传递给 MPI 例程,这是非常低效的(在这种情况下并不重要)。

【讨论】:

感谢答案和旗帜上的提示,以及数组!很奇怪;该代码在阻止发送/接收时运行良好。 离题:-fsanitize=address 到底是做什么的? 它激活了这个的使用:code.google.com/p/address-sanitizer/wiki/AddressSanitizer

以上是关于MPI 非阻塞发送/接收中的请求数组的主要内容,如果未能解决你的问题,请参考以下文章

MPI 非阻塞发送和接收排序

用于未知消息大小的 MPI 非阻塞发送和接收以及 mpi_iprobe()

发送多个 mpi 非阻塞发送 - 它会保留发送的顺序吗

一个不安全的 MPI 非阻塞通信示例?

同步异步阻塞非阻塞的概念理解

非阻塞点对点通信中发送模式对 MPI 开销的影响