MPI

又称为:Message Passing Interface 消息传递函数库


MPI的实现有:

  • MPICH(MPI最流行的非专利实现)
  • MVAPICH

简单案例:

  1. Hello
1
2
3
4
5
6
7
8
9
10
11
12
#include <stdio.h>
#include "mpi.h"
main(int argc, char *argv[])
{
int myid, numprocs;

MPI_Init(&argc, &argv);
MPI_Comm_rank(MPI_COMM_WORLD, &myid);
MPI_Comm_size(MPI_COMM_WORLD, &numprocs);
printf("I am %d of %d \n", myid, numprocs);
MPI_Finalize();
}

1
2
3
4
5
结果:
I am 0 of 4
I am 2 of 4
I am 1 of 4
I am 3 of 4

3种MPI参数:

  • IN: 参数在函数的调用中不会被修改
  • OUT: 参数在函数的调用中会被修改
  • INOUT: 参数在一些函数为IN,另一些为OUT

MPI初始化与MPI结束

  • MPI_INIT是MPI程序的第一个调用
  • 启动MPI环境,标志并行代码的开始
  • MPI_FINALIZE是最终程序,最后一个调用

MPI程序的编译和运行

1
2
3
mpicc - o hello hello.c
mpirun - np 4 . / hello
mpiexec - np 4 . / hello
  • 生成hello的可执行代码
    -
  • 指定np的值,表示进程数,由用户指定, 这里是4
    -
  • 这里比较关键的三个API是 mpicc, mpic++, mpirun, mpiexec

写MPI程序须知的两个问题:

  1. 任务由多少进程来进行并行计算 MPI_Comm_size得知进程个数
  2. 我是哪一个进程 MPI_Comm_rank得到0 - P - 1的整数,相当于进程ID

有消息传递greetings:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <stdio.h>
#include "mpi.h"
main(int argc, char *argv[])
{
int myid; //进程数
int numprocs; //进程ID
MPI_Status status; //消息接受状态变量
char message[100]; //消息buffer,存储也是分布的。

MPI_Init(&argc, &argv); //初始化MPI
MPI_Comm_rank(MPI_COMM_WORLD, &myid); //该函数被各进程各调用一次,得到rank值
MPI_Comm_size(MPI_COMM_WORLD, &numprocs); //该函数被各进程调用一次,得到进程数
if (myid != 0 ) {
sprintf(message, "Greetings from process %d!", myid); //建立消息
MPI_Send(message, strlen(message) + 1, MPI_CHAR, 0, 99, MPI_COMM_WORLD) //发送长度strlen(message)+1,使得\0也一起发出去。
} else { /* myid ==0 */
for (source = 1 ; source < numprocs ; source++) {
MPI_Recv(message, 100, MPI_CHAR, source, 99, MPI_COMM_WORLD, &status);
printf("%s\n", message);
}
}
/*关闭MPI,MPI代码结束*/
MPI_Finalize();
}/* end main */
1
2
3
4
5
结果:
Greetings from process 1!
Greetings from process 2!
Greetings from process 3!
为什么进程1,进程2,进程3依次向进程0发消息?

代码理解:

  • 通信组 / 通信子:MPI_COMM_WORLD
  • 一个通信组是一个进程组的集合。所有参与并行计算的进程可以组合为一个或多个通信组。
  • 执行MPI_init后,一个MPI程序的所有进程形成一个缺省的组。这个组被写作MPI_COMM_WORLD
  • MPI_Comm_size获得通信组comm中包含的进程数
  • MPI_Comm_rank得到本进程在通信组中的rank值
1
2
3
数据传送 +同步操作
MPI_Send(A, 10, MPI_DOUBLE, 1, 99, MPI_COMM_WORLD);
MPI_Recv(B, 20, MPI_DOUBLE, 0, 99, MPI_COMM_WORLD, &STATUS)

6个最基本的MPI程序

  • MPI_Init()
  • MPI_Comm_size()
  • MPI_Comm_rank()
  • MPI_Send()
  • MPI_Recv()
  • MPI_Finalize()

Glossary

  • Blocking
  • non - blocking
1
int MPI_Send(void*buf, int count, MPI_Datatype datatype, int dest, int tag, MPI_Commcomm);

buf 发送缓冲区的起始地址
count 发送信息的元素个数
datatype 数据类型
dest 目标进程的rank值
tag 消息标签
comm 通信组


1
int MPI_Recv(void*buf, int count, MPI_Datatype datatype, int source, int tag, MPI_Commcomm, MPI_Status *status);

OUT buf 发送缓冲区的起始地址
count 发送信息的元素个数
datatype 数据类型
dest 目标进程的rank值
tag 消息标签
comm 通信组
OUT status 包含实际收到的信息的有关信息


在消息传递中使用标签,是为了防止顺序混乱