MPI_Scatter 的介绍
MPI_Scatter
是一个跟 MPI_Bcast
类似的集体通信机制。MPI_Scatter
的操作会设计一个指定的根进程,根进程会将数据发送到 communicator 里面的所有进程。MPI_Bcast
和 MPI_Scatter
的主要区别很小但是很重要。MPI_Bcast
给每个进程发送的是同样的数据,然而 MPI_Scatter
给每个进程发送的是一个数组的一部分数据。下图进一步展示了这个区别。
在图中我们可以看到,MPI_Bcast
在根进程上接收一个单独的数据元素(红色的方块),然后把它复制到所有其他的进程。MPI_Scatter
接收一个数组,并把元素按进程的秩分发出去。第一个元素(红色方块)发往进程0,第二个元素(绿色方块)发往进程1,以此类推。尽管根进程(进程0)拥有整个数组的所有元素,MPI_Scatter
还是会把正确的属于进程0的元素放到这个进程的接收缓存中。下面的 MPI_Scatter
函数的原型。
MPI_Scatter(
void* send_data,
int send_count,
MPI_Datatype send_datatype,
void* recv_data,
int recv_count,
MPI_Datatype recv_datatype,
int root,
MPI_Comm communicator)
这个函数看起来确实很大很吓人,别怕,我们来详细解释一下。第一个参数,send_data
,是在根进程上的一个数据数组。第二个和第三个参数,send_count
和 send_datatype
分别描述了发送给每个进程的数据数量和数据类型。如果 send_count
是1,send_datatype
是 MPI_INT
的话,进程0 会得到数据里的第一个整数,以此类推。如果send_count
是2的话,进程0 会得到前两个整数,进程1会得到第三个和第四个整数,以此类推。在实践中,一般来说 send_count
会等于数组的长度除以进程的数量。除不尽怎么办?我们会在后面的课程中讲这个问题。
函数定义里面接收数据的参数跟发送的参数几乎相同。recv_data
参数是一个缓存,它里面存了recv_count
个 recv_datatype
数据类型的元素。最后两个参数,root
和 communicator
分别指定开始分发数组的了根进程以及对应的 communicator。
MPI_Gather 的介绍
MPI_Gather
跟 MPI_Scatter
是相反的。MPI_Gather
从好多进程里面收集数据到一个进程上面而不是从一个进程分发数据到多个进程。这个机制对很多并行算法很有用,比如并行的排序和搜索。下图是这个算法的一个示例。
跟 MPI_Scatter
类似,MPI_Gather
从其他进程收集元素到根进程上面。元素是根据接收到的进程的秩排序的。MPI_Gather
的函数原型跟 MPI_Scatter
长的一样。
MPI_Gather(
void* send_data,
int send_count,
MPI_Datatype send_datatype,
void* recv_data,
int recv_count,
MPI_Datatype recv_datatype,
int root,
MPI_Comm communicator)
在 MPI_Gather
中,只有根进程需要一个有效的接收缓存。所有其他的调用进程可以传递 NULL
给recv_data
。另外,别忘记 recv_count
参数是从每个进程接收到的数据数量,而不是所有进程的数据总量之和。这一点对MPI初学者来说经常容易搞错。
使用 MPI_Scatter 和 MPI_Gather 来计算平均数
到目前为止,我们讲解了两个用来操作 多对一 或者 一对多 通信模式的 MPI 方法,也就是说多个进程要么向一个进程发送数据,要么从一个进程接收数据。很多时候发送多个元素到多个进程也很有用(也就是多对多通信模式)。MPI_Allgather
就是这个作用。
对于分发在所有进程上的一组数据来说,MPI_Allgather
会