Fortran + MPI:Gatherv 问题
Fortran + MPI: Issue with Gatherv
我正在尝试使用 Scatterv 分发二维数组,效果很好。但是对应的Gatherv操作报错:message truncated。谁能解释一下我做错了什么。
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
问题是发送的数据量大于根告诉 MPI 它期望的数据量。您创建了一个名为 sendcounts
的数组,其中包含一些计数,根进程将使用这些计数将数组中的空间分配给不同的等级,但是每个进程都在发送 mpi_size
,这可能大于某些发送计数(例如 2)。您需要确保数字匹配。您可以找到示例代码 here.
此处显示的代码中存在多个错误。
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) gather操作反过来也是如此。但是在那种情况下,除了一个(对于等级 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)
注意 sendcounts(mpi_rank+1)
中 +1
的使用。 MPI 等级从 0 开始编号,而 Fortran 数组索引从 1 开始,除非另有说明。
此外,您不应使用 mpi_
前缀来命名您自己的 subroutines/functions/modules/variables,以防止与真正的 MPI 符号发生名称冲突。
我正在尝试使用 Scatterv 分发二维数组,效果很好。但是对应的Gatherv操作报错:message truncated。谁能解释一下我做错了什么。
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
问题是发送的数据量大于根告诉 MPI 它期望的数据量。您创建了一个名为 sendcounts
的数组,其中包含一些计数,根进程将使用这些计数将数组中的空间分配给不同的等级,但是每个进程都在发送 mpi_size
,这可能大于某些发送计数(例如 2)。您需要确保数字匹配。您可以找到示例代码 here.
此处显示的代码中存在多个错误。
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) gather操作反过来也是如此。但是在那种情况下,除了一个(对于等级 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)
注意 sendcounts(mpi_rank+1)
中 +1
的使用。 MPI 等级从 0 开始编号,而 Fortran 数组索引从 1 开始,除非另有说明。
此外,您不应使用 mpi_
前缀来命名您自己的 subroutines/functions/modules/variables,以防止与真正的 MPI 符号发生名称冲突。