分类: 云计算
2012-07-16 20:39:56
集体通信(collective communication)涉及了在多个进程间发送和接收数据。一般来说,数据在多个进程间的流动都可以通过MPI发送和接收例程来完成。然而,一些通 信操作的序列非常普遍,导致MPI提供了一堆集合通信例程来处理它们。这些例程通过点对点通信构建起来。即使你可以构建自己的集合通信例程,然而这些“黑盒”例程隐藏了许多杂乱的细节,并经常为那个操作提供最高效的算法实现。
集体通信例如在一个组里的多个进程之间传送数据。要注意集体通信调用没有使用相关的发送/接收调用的标签机制。相反它们和程序执行的顺序关联。因此,用户必须保证所有的处理器执行相同的集合通信调用,并以相同的顺序执行它们。
集合通信例程支持所有进程或只是指定进程集之间的数据动作。通信者用来标识涉及的进程集合。
栅栏同步(Barrier Synchronization)
有些情况一些进程必须等待其它进程完成它们当前的指令后才可以继续执行。一个常见例子是根进程读取数据然后把这些数据发送给其它进程。其它进程必须等待I/O的完成和数据的移动。
MPI_Barrier例程会阻塞调用进程,直到所有组进程调用了这个函数。当MPI_Barrier返回时,所有进程都在这个栅栏上同步了。
MPI_Barrier由软件完成,在一些机器上可能造成显著的开销。通常,你应当只在需要的时候使用栅栏。
MPI_Barrier的函数原型:
int MPI_Barrier(MPI_Comm comm);
广播(Broadcast)
MPI_Bcast例程把根进程内存里的数据拷贝到通信者里的其它进程的相同内存地址里。它的函数原型为:
MPI_Bast(void *send_buffer, int send_count, MPI_Datatype send_type, int root_rank, MPI_COMM comm);
例:
send_count = 1;
root = 0;
MPI_Bcast(&a, &send_count, MPI_INT, root, comm);
缩减(Reduction)
MPI_Reduce例程可以:
1、从每个进程收集数据;
2、把这些数据缩减成单个的值(比如求和或最大值);
3、把缩减后的值存储到根进程里。
它的函数原型为:
int MPI_Reduce(void *send_buffer, void *recv_buffer, int count, MPI_Datatype datatype, MPI_Op operation, int root_rank, MPI_Comm comm);
所有进程(包括根进程)的send_buffer中的值,缩减成为一个值后,放入根进程的recv_buffer里。send_buffer和recv_buffer的元素个数都为count。
例:
count = 1;
rank = 0;
MPI_Reduce(&a, &x, count, MPI_REAL, MPI_SUM, rank, MPI_COMM_WORLD);
MPI_Reduce的预定义的操作有:
操作 | 描述 |
---|---|
MPI_MAX | 最大值 |
MPI_MIN | 最小值 |
MPI_SUM | 求和 |
MPI_PROD | 求积 |
MPI_LAND | 逻辑与 |
MPI_BAND | 位与 |
MPI_LOR | 逻辑或 |
MPI_BOR | 位或 |
MPI_LXOR | 逻辑异或 |
MPI_BXOR | 位异或 |
MPI_MINLOC | 计算一个全局最小值和附到这个最小值上的索引--可以用来决定包含最小值的进程的秩 |
MPI_MAXLOC | 计算一个全局最大值和附到这个最大值上的索引--可以用来决定包含最小值的进程的秩 |
收集(Gather)
有两个收集操作:MPI_Gather和MPI_Allgather。
MPI_Gather例程是一个多对一的通信。MPI_Gather有和对应的分散例程一样的参数。接收参数只对根进程有意义。
当MPI_Gather被调用时,每个进程(包括根进程)把它的发送缓冲的内容发送到根进程。根进程以秩顺序接收并存储它们。
收集操作也可以用一种方式完成:每个进程调用MPI_Send、根进程调用N次的MPI_Recv来接收所有的消息。
函数原型:
int MPI_Gather(void *send_buffer, int send_count, MPI_Datatype send_type, void *recv_buffer, int recv_count, MPI_Datatype recv_type, int rank, MPI_Comm comm);
例:
send_count = 1;
recv_count = 1;
recv_rank = 0;
MPI_Gather(&a, send_count, MPI_REAL, &a, recv_count, MPI_REAL, recv_rank, MPI_COMM_WORLD);
MPI_Allgather相当于MPI_Gather和MPI_Bcast操作的组合。数据从各进程中收集,而后除了根进程外,所有的进程也都会接收收集后的结果。
MPI_Allgather和MPI_Gather的参数一样。
分散(Scatter)
MPI_Scatter是一个一对多的通信。不同的数据由根进程(以秩顺序)发送到每个进程。
当MPI_Scatter调用,根进程把一堆连续的内存分解为相等的块,并把每个块发送到各个进程。结果和根进程执行N次MPI_Send操作以及每个进程执行一个MPI_Recv的结果相同。
发送参数只对根进程有意义。
函数原型:
int MPI_Scatter(void *send_buffer, int send_count, MPI_Datatype send_type, void *recv_buffer, int recv_count, MPI_Datatype recv_type, int root_rank, MPI_Comm comm);
例:
send_count = 1;
recv_count = 1;
send_rank = 0;
MPI_Scatter(&a, send_count, MPI_REAL, &a, recv_count, MPI_REAL, send_rank, MPI_COMM_WORLD);
高级操作(Advanced Operation)
MPI_Allreduce:用于合并每个进程的输入缓冲的元素。
用户定义缩减操作:缩减可以定义为任意操作。
收集/分散矢量操作:MPI_Gatherv和MPI_Scatterv支持各个进程的可变数量的数据的收集和分散。
其它收集/分散变体:
1、MPI_Allgather和MPI_Alltoall。
2、不指定根进程:所有的进程都收集或分散数据。
3、发送和接收参数对所有进程有意义。
MPI_Scan:用于执行贯穿于组的数组上的前缀缩减;返回缩减的结果给所有进程。
MPI_Reduce_scatter:合并了MPI_Reduce和MPI_Scatterv。