MPI简介
说到并行计算,我们有一个不可绕开的话题——MPI编程。MPI是一个跨语言的通讯协议,用于编写并行计算机。支持点对点和广播。MPI是一个信息传递应用程序接口,包括协议和和语义说明,他们指明其如何在各种实现中发挥其特性。MPI的目标是高性能,大规模性,和可移植性。MPI在今天仍为高性能计算的主要模型。与OpenMP并行程序不同,MPI是一种基于信息传递的并行编程技术。消息传递接口是一种编程接口标准,而不是一种具体的编程语言。简而言之,MPI标准定义了一组具有可移植性的编程接口。
MPI基本函数
MPI调用借口的总数虽然庞大, 但根据实际编写MPI的经验, 常用的MPI调用的个数确什么有限。 下面是6个最基本的MPI函数。
MPI_Init(…);
MPI_Comm_size(…);
MPI_Comm_rank(…);
MPI_Send(…);
MPI_Recv(…);
MPI_Finalize();
我们在此通过一个简单的例子来说明这6个MPI函数的基本用处。
函数介绍
int MPI_Init (int* argc ,char** argv[] )
该函数通常应该是第一个被调用的MPI函数用于并行环境初始化,其后面的代码到MPI_Finalize()
函数之前的代码在每个进程中都会被执行一次。
- 除MPI_Initialized()外, 其余所有的MPI函数应该在其后被调用。
- MPI系统将通过argc,argv得到命令行参数(也就是说main函数必须带参数,否则会出错)。
在MPI_Init
的过程中,所有 MPI 的全局变量或者内部变量都会被创建。举例来说,一个通讯器 communicator 会根据所有可用的进程被创建出来(进程是我们通过 mpi 运行时的参数指定的),然后每个进程会被分配独一无二的秩 rank。当前来说,MPI_Init
接受的两个参数是没有用处的,不过参数的位置保留着,可能以后的实现会需要用到。
int MPI_Finalize (void)
- 退出MPI系统, 所有进程正常退出都必须调用。 表明并行代码的结束,结束除主进程外其它进程
- 串行代码仍可在主进程(rank = 0)上运行, 但不能再有MPI函数(包括MPI_Init())。
int MPI_Comm_size (MPI_Comm comm ,int* size )
- 获得进程个数 size。
- 指定一个通信子,也指定了一组共享该空间的进程, 这些进程组成该通信子的group(组)。
- 获得通信子comm中规定的group包含的进程的数量。
MPI_Comm_size
会返回 communicator 的大小,也就是 communicator 中可用的进程数量。在我们的例子中,MPI_COMM_WORLD
(这个 communicator 是 MPI 帮我们生成的)这个变量包含了当前 MPI 任务中所有的进程,因此在我们的代码里的这个调用会返回所有的可用的进程数目。
int MPI_Comm_rank (MPI_Comm comm ,int* rank)
- 得到本进程在通信空间中的rank值,即在组中的逻辑编号(该 rank值为0到p-1间的整数,相当于进程的ID。)
MPI_Comm_rank
这个函数会返回 communicator 中当前进程的 rank。 communicator 中每个进程会以此得到一个从0开始递增的数字作为 rank 值。rank 值主要是用来指定发送或者接受信息时对应的进程。
MPI_Send参数说明
int MPI_Send( void *buff, int count, MPI_Datatype datatype, int dest, int tag, MPI_Comm comm)
void *buff
:你要发送的变量。int count
:你发送的消息的个数
注意:不是长度,例如你要发送一个int整数,这里就填写1,如要是发送“hello”字符串,这里就填写6(C语言中字符串未有一个结束符,需要多一位)MPI_Datatype datatype
:你要发送的数据类型,这里需要用MPI定义的数据类型,可在网上找到,在此不再罗列。int dest
:目的地进程号,你要发送给哪个进程,就填写目的进程的进程号。int tag
:消息标签,接收方需要有相同的消息标签才能接收该消息。MPI_Comm comm
:通讯域。表示你要向哪个组发送消息。
MPI_Recv参数说明
int MPI_Recv( void *buff, int count, MPI_Datatype datatype, int source, int tag, MPI_Comm comm, MPI_Status *status)
void *buff
:你接收到的消息要保存到哪个变量里。int count
:你接收消息的消息的个数
注意:不是长度,例如你要发送一个int整数,这里就填写1,如要是发送“hello”字符串,这里就填写6(C语言中字符串未有一个结束符,需要多一位)它是接收数据长度的上界. 具体接收到的数据长度可通过调用MPI_Get_count 函数得到。MPI_Datatype datatype
:你要接收的数据类型,这里需要用MPI定义的数据类型,可在网上找到,在此不再罗列。int dest
:接收端进程号,你要需要哪个进程接收消息就填写接收进程的进程号。int tag
:消息标签,需要与发送方的tag值相同的消息标签才能接收该消息。MPI_Comm comm
:通讯域。MPI_Status *status
:消息状态。接收函数返回时,将在这个参数指示的变量中存放实际接收消息的状态信息,包括消息的源进程标识,消息标签,包含的数据项个数等。
MPI常用的数据类型
MPI datatype | C equivalent |
---|---|
MPI_SHORT | short int |
MPI_INT | int |
MPI_LONG | long int |
MPI_LONG_LONG | long long int |
MPI_UNSIGNED_CHAR | unsigned char |
MPI_UNSIGNED_SHORT | unsigned short int |
MPI_UNSIGNED | unsigned int |
MPI_UNSIGNED_LONG | unsigned long int |
MPI_UNSIGNED_LONG_LONG | unsigned long long int |
MPI_FLOAT | float |
MPI_DOUBLE | double |
MPI_LONG_DOUBLE | long double |
MPI_CHAR | char |
示例
基本函数都已经介绍完,现在我们来用一个示例来加强对这些基本函数的理解。
#include <stdio.h>
#include <string.h> /* For strlen */
#include <mpi.h> /* For MPI functions, etc */
const int MAX_STRING = 100;
int main(void) {
char greeting[MAX_STRING]; /* String storing message */
int comm_sz; /* Number of processes */
int my_rank; /* My process rank */
/* Start up MPI */
MPI_Init(NULL, NULL);
/* Get the number of processes */
MPI_Comm_size(MPI_COMM_WORLD, &comm_sz);
/* Get my rank among all the processes */
MPI_Comm_rank(MPI_COMM_WORLD, &my_rank);
if (my_rank != 0) {
/* Create message */
sprintf(greeting, "Greetings from process %d of %d!", my_rank, comm_sz);
/* Send message to process 0 */
MPI_Send(greeting, strlen(greeting)+1, MPI_CHAR, 0, 0, MPI_COMM_WORLD);
} else {
/* Print my message */
printf("Greetings from process %d of %d!\n", my_rank, comm_sz);
for (int q = 1; q < comm_sz; q++) {
/* Receive message from process q */
MPI_Recv(greeting, MAX_STRING, MPI_CHAR, q,
0, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
/* Print message from process q */
printf("%s\n", greeting);
}
}
/* Shut down MPI */
MPI_Finalize();
return 0;
} /* main */
运行结果如下图所示
可以看到,当笔者开启四线程运行时,1-3号进程发送消息,0号进程接收到消息并打印;当笔者开启八线程运行时,1-7号进程发送消息,0号进程接收到消息并打印。
本文使用的是标准阻塞接收发送的方式。消息传递是MPI的特性,也是我们学习的难点。这我们学习MPI必须掌握的。
消息发送与接收函数的参数的一些重要说明。
- MPI标识一条消息的信息包含四个域:
- 源:发送进程隐式确定,进程rank值唯一标识.
- 目的:Send函数参数确定.
- Tag:Send函数参数确定, (0,UB) 232-1.
- 通信子:缺省MPI_COMM_WORLD
- Group:有限/N, 有序/Rank [0,1,2,…N-1]
- Contex:Super_tag,用于标识该通讯空间.
- buffer的使用
buffer必须至少可以容纳count个由datatype指明类型的数据. 如果接收buf太小, 将导致溢出、 出错 - 消息匹配
- 参数匹配source,tag,comm/dest,tag,comm.
- Source == MPI_ANY_SOURCE: 接收任意处理器来的数据(任意消息来源).
- Tag == MPI_ANY_TAG: 匹配任意tag值的消息(任意tag消息).
- 在阻塞式消息传送中不允许Source == dest,否则会导致死锁.
- 消息传送被限制在同一个通信域内。
- 在send函数中必须指定唯一的接收者。
原文链接
版权属于:KevinBean
本文链接:https://www.kevinbean.top/index.php/default/348.html
转载时须注明出处及本声明