本文主要总结MPI消息传递的一些基本概念

MPI的工作模式

目前我对MPI的工作模式的理解就是他其实呢是把一份程序传送到不同的进程中,通过MPI的接口我们可以获取当前进程中的一些信息,例如进程号,进程总数等,这样我们就可以在程序中显式的对进程信息进行判断,然后做不同的处理,这样同一份程序在不同的进程中做的事情是不一样的,尽管代码(变量,函数等)都是一样的,可见这种并行的粒度是很大的。

下面引用下书里的一段话:

MPI程序工作模式
在不同工作模式下可以适合SPMD或者MPMD等体系结构。以串行方式编写,运行时分别执行不同的块。

这里的不同的块其实就是通过MPI提供的接口获取的进程信息进而写出的条件判断语句。

所有程序元素,只要没进行显式区分,不论是代码,函数,全局变量还是局部变量,都默认的有全部进程所共有,所有进程里看到的虽然是相同的名字,但在“物理”上却彼此无关。同一个变量,在不同的进程空间看来,是物理上互不联系的,属于各个进程的私有变量。当然,从语义角度看,在逻辑上他们则应属于描述同一事物的不同部分,或同为构成事物模型的某种属性/特征等。

上面这段虽然说的很高大上,但是本质还是我最开始理解的那些啦。

MPI程序编写的难点就在于显式的条件判断来制定不同进程上执行的不同的代码块。

MPI消息传递

这里我就总结下MPI的消息部分。
在通信过程中,MPI消息被封装在“信封”中,然后经过MPI环境自己的缓冲区在交由网络传输层打包发送。
基本的MPI消息传递封装大致代码总结可以如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include "mpi.h"

// 初始化MPI环境
MPI_Init(&argc, &argv);

// 获取当前进程的进程号
MPI_Comm_rank(MPI_COMM_WORLD, &rank);

// 获取当前所有进程个数
MPI_Comm_size(MPI_COMM_WORLD, &size);

MPI_Send(buf, size, datatype, target, tag, MPI_COMM_WORLD);
MPI_Recv(buf, size, datatype, source, tag, MPI_COMM_WORLD, &status);

MPI_Finalize();

通过发送和接收消息的接口参数就可以看出,这些消息可以跟信件的收发一一联系起来。
buf, size, datatype:这些是被信封包起来的数据部分。
target:是收件人的信息,即邮编和地址,收件人姓名等。
source:是寄件人的信息,邮编、地址、姓名等。
tag:邮戳,这样就可以区分信件的顺序。
MPI_COMM_WORLD:邮局名称,收发信件通过的邮局。

上面代码中有个status,他是一个MPI定义的结构,用来保存执行结果(状态):

1
2
3
4
5
6
7
typedef struct MPI_Status {
int cout; // 记录发送/接收的字节数
int cancelled;
int MPI_SOURCE; // 记录接收消息的源进程
int MPI_TAG; // 记录消息的标志值
int MPI_ERROR;
};

当我们使用MPI_ANY_SOURCEMPI_ANY_TAG的时候可以通过这个结构来判断接收到的数据来源。

点到点通信

点到点通信要求必须有sendrecv配对。消息通信的数据传递流程如下:

  1. 发送端发起数据发送调用MPI_??Send
  2. MPI环境从发送缓冲区提取要发送的数据,据此组装发送消息。
  3. 将组装的消息发动个目标进程。
  4. 接收端收取可匹配的消息,并将其解析到自己的接收缓冲区中。

Comments