您什么时候会使用不同的计数或类型来发送和接收进程?
When would you use different counts or types for sending and receiving processes?
MPI 中描述发送和接收的许多例程 - MPI_Sendrecv、MPI_Scatter 等 - 具有用于 both 发送的计数和类型的参数和接收。例如,在 Fortran 中 MPI_Scatter 的签名是:
MPI_SCATTER(SENDBUF, SENDCOUNT, SENDTYPE,
RECVBUF, RECVCOUNT, RECVTYPE, ROOT, COMM, IERROR)
如果发送的数据量必须与接收的数据量相同,为什么两者都需要?这不只是引入了不一致的可能性吗?
需要不同 counts/types 的用例是什么?
对于发送方来说,这两个是一样的,但是接收方可能不知道收到了多少个元素。
当您使用 MPI 派生类型时,您可以将数组视为某些基本数字类型的 n
元素,也可以将其视为某些 MPI 派生类型的一个或多个元素。在那种情况下,不仅计数而且数据类型都可以不同,尽管它们对应于相同的缓冲区。
另一方面,它不受通信器中进程数的影响。通信器大小始终是隐式的,您在调用集体时不会在任何地方直接输入它。
MPI 要求发送和接收进程就数据类型和数量达成一致(有点;对于点对点通信,接收方可以请求 比发送的多)。但是 MPI 数据类型还描述了数据在内存中的布局,这是发送方和接收方需要使用不同类型的一个很常见的原因。
你问的特别是Scatter和Fortran,我们来看看那个案例。让我们考虑按行将 size*n
矩阵分散到不同的进程
|---n---| ---
0 0 0 0 |
a = 1 1 1 1 size
2 2 2 2 |
---
因此排名 0 获得 [0 0 0 0]
,排名 1 获得 [1 1 1 1]
,等等
在 Fortran 中,它们在内存中不连续;所以要描述一行,你必须使用 MPI_Type_vector
:
call MPI_Type_vector(n, 1, size, MPI_REAL, row_type, ierr)
这描述了 n 个实数,但每个实数之间由大小实数分隔。
另一方面,如果接收进程只是将数据接收到一维数组中:
real, dimension(n) :: b
那么它可以不使用那个类型来描述数据; b
没有足够的空间容纳 n
个实数,每个实数之间有 size
的间隙!它想像 n * MPI_REAL
一样接收数据。如果您必须发送数据列,这种不匹配在 C 中也是一样的。
因此,这是以不同方式指定数据类型(以及计数)的常见原因;对于 scatter-er,必须使用一种数据类型来描述数据,该数据类型包括保存要发送的值的较大数据结构的布局;但是 scatter-ee 很可能会将数据接收到具有不同布局的不同数据结构中。
下面是一个简单的工作示例。
program scatterdemo
use mpi
implicit none
real, allocatable, dimension(:,:) :: a
real, allocatable, dimension(:) :: b
integer :: ierr, rank, comsize
integer, parameter :: n=4
integer :: i
integer :: row_type, row_type_sized, real_size
integer(kind=MPI_ADDRESS_KIND) :: lb=0, extent
call MPI_Init(ierr)
call MPI_Comm_rank(MPI_COMM_WORLD, rank, ierr)
call MPI_Comm_size(MPI_COMM_WORLD, comsize, ierr)
if (rank == 0) then
allocate( a(comsize, n) )
do i=1,comsize
a(i,:) = i-1
enddo
endif
allocate( b(n) )
call MPI_Type_size(MPI_REAL, real_size, ierr)
call MPI_Type_vector(n, 1, comsize, MPI_REAL, row_type, ierr)
extent = real_size*1
call MPI_Type_create_resized(row_type, lb, extent, row_type_sized, ierr)
call MPI_Type_commit(row_type_sized, ierr)
call MPI_Scatter(a, 1, row_type_sized, b, n, MPI_REAL, 0, MPI_COMM_WORLD, ierr)
print *, rank, b
if (rank == 0) deallocate (a)
deallocate(b)
call MPI_Finalize(ierr)
end program scatterdemo
和运行它有六个处理器给出
$ mpirun -np 6 ./scatter
0 0.0000000E+00 0.0000000E+00 0.0000000E+00 0.0000000E+00
3 3.000000 3.000000 3.000000 3.000000
1 1.000000 1.000000 1.000000 1.000000
5 5.000000 5.000000 5.000000 5.000000
2 2.000000 2.000000 2.000000 2.000000
4 4.000000 4.000000 4.000000 4.000000
MPI 中描述发送和接收的许多例程 - MPI_Sendrecv、MPI_Scatter 等 - 具有用于 both 发送的计数和类型的参数和接收。例如,在 Fortran 中 MPI_Scatter 的签名是:
MPI_SCATTER(SENDBUF, SENDCOUNT, SENDTYPE,
RECVBUF, RECVCOUNT, RECVTYPE, ROOT, COMM, IERROR)
如果发送的数据量必须与接收的数据量相同,为什么两者都需要?这不只是引入了不一致的可能性吗?
需要不同 counts/types 的用例是什么?
对于发送方来说,这两个是一样的,但是接收方可能不知道收到了多少个元素。
当您使用 MPI 派生类型时,您可以将数组视为某些基本数字类型的 n
元素,也可以将其视为某些 MPI 派生类型的一个或多个元素。在那种情况下,不仅计数而且数据类型都可以不同,尽管它们对应于相同的缓冲区。
另一方面,它不受通信器中进程数的影响。通信器大小始终是隐式的,您在调用集体时不会在任何地方直接输入它。
MPI 要求发送和接收进程就数据类型和数量达成一致(有点;对于点对点通信,接收方可以请求 比发送的多)。但是 MPI 数据类型还描述了数据在内存中的布局,这是发送方和接收方需要使用不同类型的一个很常见的原因。
你问的特别是Scatter和Fortran,我们来看看那个案例。让我们考虑按行将 size*n
矩阵分散到不同的进程
|---n---| ---
0 0 0 0 |
a = 1 1 1 1 size
2 2 2 2 |
---
因此排名 0 获得 [0 0 0 0]
,排名 1 获得 [1 1 1 1]
,等等
在 Fortran 中,它们在内存中不连续;所以要描述一行,你必须使用 MPI_Type_vector
:
call MPI_Type_vector(n, 1, size, MPI_REAL, row_type, ierr)
这描述了 n 个实数,但每个实数之间由大小实数分隔。
另一方面,如果接收进程只是将数据接收到一维数组中:
real, dimension(n) :: b
那么它可以不使用那个类型来描述数据; b
没有足够的空间容纳 n
个实数,每个实数之间有 size
的间隙!它想像 n * MPI_REAL
一样接收数据。如果您必须发送数据列,这种不匹配在 C 中也是一样的。
因此,这是以不同方式指定数据类型(以及计数)的常见原因;对于 scatter-er,必须使用一种数据类型来描述数据,该数据类型包括保存要发送的值的较大数据结构的布局;但是 scatter-ee 很可能会将数据接收到具有不同布局的不同数据结构中。
下面是一个简单的工作示例。
program scatterdemo
use mpi
implicit none
real, allocatable, dimension(:,:) :: a
real, allocatable, dimension(:) :: b
integer :: ierr, rank, comsize
integer, parameter :: n=4
integer :: i
integer :: row_type, row_type_sized, real_size
integer(kind=MPI_ADDRESS_KIND) :: lb=0, extent
call MPI_Init(ierr)
call MPI_Comm_rank(MPI_COMM_WORLD, rank, ierr)
call MPI_Comm_size(MPI_COMM_WORLD, comsize, ierr)
if (rank == 0) then
allocate( a(comsize, n) )
do i=1,comsize
a(i,:) = i-1
enddo
endif
allocate( b(n) )
call MPI_Type_size(MPI_REAL, real_size, ierr)
call MPI_Type_vector(n, 1, comsize, MPI_REAL, row_type, ierr)
extent = real_size*1
call MPI_Type_create_resized(row_type, lb, extent, row_type_sized, ierr)
call MPI_Type_commit(row_type_sized, ierr)
call MPI_Scatter(a, 1, row_type_sized, b, n, MPI_REAL, 0, MPI_COMM_WORLD, ierr)
print *, rank, b
if (rank == 0) deallocate (a)
deallocate(b)
call MPI_Finalize(ierr)
end program scatterdemo
和运行它有六个处理器给出
$ mpirun -np 6 ./scatter
0 0.0000000E+00 0.0000000E+00 0.0000000E+00 0.0000000E+00
3 3.000000 3.000000 3.000000 3.000000
1 1.000000 1.000000 1.000000 1.000000
5 5.000000 5.000000 5.000000 5.000000
2 2.000000 2.000000 2.000000 2.000000
4 4.000000 4.000000 4.000000 4.000000