我正在尝试使用Scatterv分发2D数组,效果很好。但是,相应的Gatherv操作会出错:消息被截断。有人可以解释我做错了什么。
program scatterv
use mpi
implicit none
integer, allocatable, dimension(:,:) :: array
integer, allocatable, dimension(:) :: chunk
integer, allocatable, dimension(:) :: displacement
integer, allocatable, dimension(:) :: sendcounts
integer :: mpi_ierr, mpi_rank, mpi_size
integer, parameter :: kWidth=4
call MPI_INIT(mpi_ierr)
call MPI_COMM_RANK(MPI_COMM_WORLD, mpi_rank, mpi_ierr)
call MPI_COMM_SIZE(MPI_COMM_WORLD, mpi_size, mpi_ierr)
if (mpi_rank == 0) then
allocate(array(mpi_size, kWidth))
allocate(displacement(mpi_size))
allocate(sendcounts(mpi_size))
displacement = (/0, 0, 0, 0, 0, 0, 0, 0, 0, 0/)
sendcounts = (/2, 10, 5, 8, 5, 2, 2, 2, 2, 2/)
endif
allocate(chunk(mpi_size))
call MPI_SCATTERV(array, sendcounts, displacement, MPI_INTEGER, chunk, mpi_size, MPI_INTEGER, 0, MPI_COMM_WORLD, mpi_ierr)
...
call MPI_GATHERV(chunk, mpi_size, MPI_INTEGER, array, sendcounts, displacement, MPI_INTEGER, 0, MPI_COMM_WORLD, mpi_ierr)
if (mpi_rank == 0) then
deallocate(array)
deallocate(displacement)
end if
deallocate(chunk)
call MPI_FINALIZE(mpi_ierr)
end program scatterv
答案 0 :(得分:3)
此处提供的代码中存在多个错误。
1)所有的位移是相等的:
if (mpi_rank == 0) then
...
displacement = (/0, 0, 0, 0, 0, 0, 0, 0, 0, 0/)
sendcounts = (/2, 10, 5, 8, 5, 2, 2, 2, 2, 2/)
endif
MPI标准要求发送缓冲区中的任何位置都不应被读取两次,并且接收缓冲区中的任何位置都不应写入两次。换句话说,所有块必须是不相交的。仅当相应的发送计数为0
(零)时,才允许位移相等。
由于性能原因,某些(如果不是大多数)MPI库不强制执行此条件。它可能有效,可能不起作用,全部取决于用于传输数据的设备。即使它有效,它仍然是不正确的MPI。
2)MPI_SCATTERV
中的接收计数与块大小不匹配:
call MPI_COMM_SIZE(MPI_COMM_WORLD, mpi_size, mpi_ierr)
...
sendcounts = (/2, 10, 5, 8, 5, 2, 2, 2, 2, 2/)
...
call MPI_SCATTERV(array, sendcounts, displacement, MPI_INTEGER, &
chunk, mpi_size, MPI_INTEGER, &
0, MPI_COMM_WORLD, mpi_ierr)
对于点对点操作,可以提供大于消息实际占用的缓冲区,而集合操作则不是这样 - 发送到进程的数据量必须匹配进程指定的接收缓冲区的大小。有些实现适用于较大的缓冲区,但依赖它的程序不正确。
分散操作的唯一原因是你有10个MPI进程(根据数组初始化程序的大小判断),最大的块大小也是10。
3)这同样适用于收集操作。但在这种情况下,除了一个(对于等级1),所有发送计数都大于预期的块大小。
程序的更正版本应如下所示:
call MPI_INIT(mpi_ierr)
call MPI_COMM_RANK(MPI_COMM_WORLD, mpi_rank, mpi_ierr)
call MPI_COMM_SIZE(MPI_COMM_WORLD, mpi_size, mpi_ierr)
allocate(sendcounts(mpi_size))
sendcounts = (/2, 10, 5, 8, 5, 2, 2, 2, 2, 2/)
if (mpi_rank == 0) then
allocate(array(mpi_size, kWidth))
allocate(displacement(mpi_size))
displacement = (/0, 2, 12, 17, 25, 27, 29, 31, 33, 35/)
endif
allocate(chunk(mpi_size))
call MPI_SCATTERV(array, sendcounts, displacement, MPI_INTEGER, &
chunk, sendcounts(mpi_rank+1), MPI_INTEGER, &
0, MPI_COMM_WORLD, mpi_ierr)
...
call MPI_GATHERV(chunk, sendcounts(mpi_rank+1), MPI_INTEGER, &
array, sendcounts, displacement, MPI_INTEGER, &
0, MPI_COMM_WORLD, mpi_ierr)
if (mpi_rank == 0) then
deallocate(array)
deallocate(displacement)
end if
deallocate(chunk)
deallocate(sendcounts)
call MPI_FINALIZE(mpi_ierr)
请注意+1
中sendcounts(mpi_rank+1)
的使用情况。除非另有说明,否则MPI排名从0开始编号,而Fortran数组索引从1开始。
此外,您不应使用mpi_
前缀来命名您自己的子例程/函数/模块/变量,以防止与真正的MPI符号发生名称冲突。
答案 1 :(得分:2)
问题是发送的数据量大于根告诉MPI预期的数据量。您创建了一个名为sendcounts
的数组,其中包含一些计数,根进程将使用这些计数将数组中的空格分配给不同的排名,但每个进程都发送mpi_size
,这可能比某些sendcounts更大(例如2)。您需要确保数字匹配。您可以找到示例代码here。