0%

MPI 小结

这段时间刚好赶上这学期的考试期,之前 MPI 只是稍微看了一点,在这里:并行编程 MPI 初探

考完一个阶段稍微轻松些了,回来把总结补上。

Message Passing Interface

并行编程的结构大致有两种:

  1. 共享内存式系统:运算核心有多个,但是多核心通过访问一个公共的内存区域来进行协作;
  2. 分布式内存系统:运算核心有多个,每个核心有它自己的内存,多个核心之间通过通信来协作,通信可以是通过网络实现的,因此分布式系统可以做到多机并行

MPI 就是一种分布式内存系统

它的全称叫做消息传递接口,就是在 C、C++、Fortran 的基础上又做了一套并行的函数库接口,然后运行时通过多进程之间的消息传递机制来协作完成并行任务。

MPI 程序框架

首先在主函数开始时调用 MPI_Init() 来建立 MPI 进程,如果程序不需要额外的输入,则直接传入 MPI_Init(NULL, NULL)

中间的代码段则是各个进程同时都会运行的部分,所以需要注意的是,MPI 程序与一般的串行程序应该是完全不一样的,因为相同的代码段会被不同的进程同时运行,而需要程序员在写代码的时候就考虑到不同进程的运行情况,写出特殊的代码,进程与进程之间通过通信子来进行相互通信交换数据。

如果只是简单地将原本的串行程序放上去,那只不过是同时在多个核上跑了很多遍一模一样的代码而已,完全达不到加速的效果。

关键的 MPI 语句运行完毕之后,如果中间有创建过另外的通信子或者组的话,还需要单独将它们释放掉。

最后,调用 MPI_Finalize() 来结束 MPI 程序。

基本函数

1
2
3
MPI_Comm_size(
MPI_Comm comm,
int* comm_size)

返回 comm 通信子中的进程总数。

1
2
3
MPI_Comm_rank(
MPI_Comm comm,
int* comm_rank)

返回当前进程在 comm 通信子中的序号。

  • 点对点通信
1
2
3
4
5
6
7
MPI_Send(
void* msg_buf,
int msg_size,
MPI_Datatype msg_type,
int dest,
int tag,
MPI_Comm comm)

dest 进程发送信息。

1
2
3
4
5
6
7
8
MPI_Recv(
void* msg_buf,
int buf_size,
MPI_Datatype buf_type,
int source,
int tag,
MPI_Comm comm,
MPI_Status* status)

source 进程接收信息。

1
2
3
4
5
MPI_Probe(
int source,
int tag,
MPI_Comm comm,
MPI_Status* status)

MPI_Recv() 很像,除了不能接收消息,其他都一样,主要是为了将下一条消息的各项属性保存到 status 里面来。

  • 集合通信
1
2
MPI_Barrier(
MPI_Comm comm)

comm 通信子中的所有进程都同步到当前位置。

1
2
3
4
5
6
MPI_Bcast(
void* data_buf,
int count,
MPI_Datatype datatype,
int source,
MPI_Comm comm)

source 进程向通信子中的所有其他进程发送消息。

MPI_Bcast() 用的是树形结构的广播,会比单纯的从 source 进程开个 for 循环要高效的多。

然而我在单台电脑上实测的时候貌似没发现 MPI_Bcast() 能快多少… 反而有时候还慢很多,不知道是不是我发送的东西太少了,还是电脑本身的处理速度太快了。

1
2
3
4
5
6
7
8
9
MPI_Scatter(
void* send_buf,
int send_count,
MPI_Datatype send_type,
void* recv_buf,
int recv_count,
MPI_Datatype recv_type,
int source,
MPI_Comm comm)

source 进程中的 send_buf 均分成多份,分发给通信子中的所有进程。
需要注意的是 send_countrecv_count 的值是一样的,都等于每个通信子将收到的数据数量。

1
2
3
4
5
6
7
8
9
MPI_Gather(
void* send_buf,
int send_count,
MPI_Datatype send_type,
void* recv_buf,
int recv_count,
MPI_Datatype recv_type,
int dest,
MPI_Comm comm)

与上面的 MPI_Scatter() 非常相似,这个的作用是从多个进程中接收一定数量的数据到 destrecv_buf 中。
同样 send_countrecv_count 的值是一样的,都等于每个通信子需要发送的数据量。


这里有完整的 MPI 函数说明。

后话

前段时间忙着准备期末考试,原本准备好的好多个文档都没看,然后之前买的书也都没怎么看。我发现我就是擅长挖坑,然后挖了坑又填不上 o ( ̄▽ ̄) d。

虽然看到现在,基本的 MPI 操作已经明白了,但是还是比较缺少实践。

接下来准备再看下 OpenMP 和 CUDA,然后找点小项目来练练手吧。