一张图说千言万语,这里有几张ASCII艺术图:
rank send buf recv buf
---- -------- --------
0 a,b,c MPI_Allgather a,b,c,A,B,C,#,@,%
1 A,B,C ----------------> a,b,c,A,B,C,#,@,%
2 #,@,% a,b,c,A,B,C,#,@,%
这只是普通的MPI_Gather,只有在这种情况下所有进程都接收数据块,即操作是无根的。
rank send buf recv buf
---- -------- --------
0 a,b,c MPI_Alltoall a,A,#
1 A,B,C ----------------> b,B,@
2 #,@,% c,C,%
(a more elaborate case with two elements per process)
rank send buf recv buf
---- -------- --------
0 a,b,c,d,e,f MPI_Alltoall a,b,A,B,#,@
1 A,B,C,D,E,F ----------------> c,d,C,D,%,$
2 #,@,%,$,&,* e,f,E,F,&,*
(如果每个元素都按发送它的等级着色,看起来会更好,但是......)
MPI_Alltoall 作为MPI_Scatter 和MPI_Gather 的组合工作 - 每个进程中的发送缓冲区像MPI_Scatter 一样被拆分,然后每一列块由各自的进程收集,其等级与块列。 MPI_Alltoall 也可以看作是全局转置操作,作用于数据块。
是否存在两种操作可以互换的情况?要正确回答这个问题,我们必须简单地分析发送缓冲区中的数据大小和接收缓冲区中的数据大小:
operation send buf size recv buf size
--------- ------------- -------------
MPI_Allgather sendcnt n_procs * sendcnt
MPI_Alltoall n_procs * sendcnt n_procs * sendcnt
接收缓冲区大小实际上是n_procs * recvcnt,但 MPI 要求发送的基本元素的数量应该等于接收的基本元素的数量,因此如果 @ 的发送和接收部分使用相同的 MPI 数据类型987654331@,那么recvcnt必须等于sendcnt。
很明显,对于相同大小的接收数据,每个进程发送的数据量是不同的。两个操作要相等,一个必要条件是两种情况下发送的缓冲区大小相等,即n_procs * sendcnt == sendcnt,只有n_procs == 1才有可能,即只有一个进程,或者@ 987654336@,即根本没有发送数据。因此,实际上不存在两种操作真正可互换的情况。但是可以通过在发送缓冲区中重复n_procs 次相同的数据来模拟MPI_Allgather 和MPI_Alltoall(正如Tyler Gill 已经指出的那样)。这是MPI_Allgather 的操作,带有一个元素的发送缓冲区:
rank send buf recv buf
---- -------- --------
0 a MPI_Allgather a,A,#
1 A ----------------> a,A,#
2 # a,A,#
这里同样使用MPI_Alltoall:
rank send buf recv buf
---- -------- --------
0 a,a,a MPI_Alltoall a,A,#
1 A,A,A ----------------> a,A,#
2 #,#,# a,A,#
反过来是不可能的——在一般情况下,不能用MPI_Allgather模拟MPI_Alltoall的动作。