并行计算的编程模型-消息传递接口(MPI)
MPI是一个标准和可移植的通信接口,MPI为并行通信提供丰富的函数接口,也为并行文件I/O访问等提供函数接口。
MPI是支持多呈许多数据(MPMD)编程模型,是一种基于库的系统,通过C和Fortran语言实现。
MPI核心:
1、 MPI的核心是进程间通信,遵循进程间顺序通信模型CSP,每个进程在自己的空间内运行。
2、 MPI声明的变量例如int b[10]是每个进程的私有变量,变量b在不同的进程中是相互独立的。
3、 MPI主要的两种通信方式为
点对点通信,即两个进程间通信
聚合通信,即一组进程间通信
每个MPI进程在每组进程中拥有一个进程号,从零开始,一组四进程的组合进程号分别为{index| index in 0, 1, 2, 3}
所有的MPI通信都在通信域中进行。
通信域包含
一组进程
一个隐藏的通信文本 (用于保证消息和库的一致性)
通信域对象是一个隐藏类型,称之为句柄。
在C语言中,通信域句柄为 MPI_Comm 类型
在Fortran语言中,通信域句柄为 Type(MPI_Comm) 类型(2008)或者INTEGER类型(早期)
在MPI程序中,存在两个预定义的通信域
MPI_COMM_WORLD 包含所有的MPI进程
MPI_COMM_SELF 仅包含一运行一个实例程序的进程
MPI提供各种通信域函数
MPI_Comm_rank 用于获取进程号 MPI_Comm_rank(*a, *b);
MPI_Comm_size 用于获取通信域的进程数量 MPI_Comm_size(*a, *b);
以及用于创建新的通信域的函数
MPI_Init 用于初始化MPI程序 MPI_Init(*a, *b);
MPI_Finalize 用于结束MPI程序 MPI_Finalize(void)
除了极个别函数外,大多数MPI函数需要在MPI_Init(or MPI_Init_thread)函数之后和MPI_Finalize之前调用
在并行程序中,若MPI_Init函数参数无法从main函数中获取命令行参数,则需要通过MPI进行指定
/* get the MPI process id and the number of process */
#include "mpi.h"
#include <stdio.h>
int main(int argc, char *argc[]){
int wrank, srank, wsize;
MPI_Init(&argc, &argv);
MPI_Comm_rank(MPI_COMM_WORLD, &wrank);
MPI_Comm_size(MPI_COMM_WORLD, &wsize);
MPI_Comm_rank(MPI_COMM_SELF, &srank);
printf("World rank %d, world size %d, self rank %d\n", wrank, wsize, srank);
MPI_Finalize();
return 0;
}
/* end class */
MPI进程间执行是相互独立的,以上代码中,printf()语句按照随机顺序执行,甚至无法保证依次输出一行。
点对点通信
发送进程需要指定发送的数据、接收数据的进程号和通信域,对一些老式消息传递系统,每个消息还需要有一个消息号,数据类型为单精度非负整数
接收进程需要指定数据接收地址、数据来源进程号、通信域和消息号,另外,可能还需要提供一个消息状态参数,用于保存数据接收的状态信息
针对早期的消息传递系统和多数文件I/O库,用包含地址和字节数的二元数组指定数据缓冲区
MPI将该二元数组扩展成三元数组,三元数组包含地址、数据类型和数据个数。
用C语言指定10个证书类型数据的缓冲区,MPI语句为(address, 10, MPI_INT)
这保证了编译者不需要知道没一种数据类型的具体字节数
也保证了该方法使MPI程序能够在混合硬件平台上运行
/* MPI允许在内存中指定不连续的数据缓冲区 */
int msg(MAX_MSG_SIZE);
...
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
MPI_Comm_size(MPI_COMM_WORLD, &size);
if (rank == 0){
for (i=1; i<size; i++)
MPI_Send(msg, msgsize, MPI_INT, i, 0, MPI_COMM_WORLD);
} else {
MPI_Recv(msg, MAX_MSG_SIZE, MPI_INT, 0, 0, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
doWork(msg);
}
/* end function */
在MPI_Send函数参数中,通信域中的进程号指定数据发送到具体进程,并提供通讯文本
消息号是一个非负整数类型,最大值取决于MPI的具体实现方式,但至少为32767
MPI_Recv接收函数与MPI_Send函数的参数类似,不同之处在于接收函数允许指定接收数据缓冲区大小大于实际发送的数据大小。
消息号和数据来源进程号可用具体数字指定,也可使用通配符类型的数值
MPI_ANY_TAG 允许用户发送一个额外证书类型的数据
MPI_ANY_SOURCE 通过非确定性算法确定进程号
MPI_STATUS_IGNORE 若不需要状态信息是使用
以上代码是0号进程向MPI_COMM_WORLD通信域中其他所有进程发送相同数据的程序示例。
该程序是MPI的基本或者标准发送模式,MPI还提供其他的数据发送模式,比如:
0、 同步发送模式
1、 就绪发送模式
2、 缓冲发送模式
3、 非阻塞式通信模式
数据类型
MPI的特性之一是所有的通信函数都带一个数据类型参数,用于描述发送和接受数据的类型,取代老式系统中仅用字节进行描述
例如,如果传输一组整数数据,MPI函数的数据类型在C语言中应该设置为MPI_INT,在Fortran中为MPI_INTEGER
使用数据类型参数的目的是:
0、 在异构环境中,例如在具有不同字节存储顺序或者不同基本数据类型长度的异构计算及系统上,保证通信顺利进行
当数据类型被指定后,MPI能够在内部转换成相应的字节数。
1、 使用户能够描述内存中非连续存放的数据并通过一个单独的MPI函数调用实现数据传输
MPI提供多种数据类型构造函数,可用来描述内存中非连续存放的数据&