这段时间刚好赶上这学期的考试期,之前MPI只是稍微看了一点,在这里:并行编程 MPI初探
考完一个阶段稍微轻松些了,回来把总结补上。
Message Passing Interface
并行编程的结构大致有两种:
- 共享内存式系统:运算核心有多个,但是多核心通过访问一个公共的内存区域来进行协作;
- 分布式内存系统:运算核心有多个,每个核心有它自己的内存,多个核心之间通过通信来协作,通信可以是通过网络实现的,因此分布式系统可以做到多机并行。
MPI就是一种分布式内存系统。
它的全称叫做消息传递接口,就是在C、C++、Fortran的基础上又做了一套并行的函数库接口,然后运行时通过多进程之间的消息传递机制来协作完成并行任务。
MPI程序框架
首先在主函数开始时调用MPI_Init()
来建立MPI进程,如果程序不需要额外的输入,则直接传入MPI_Init(NULL, NULL)
。
中间的代码段则是各个进程同时都会运行的部分,所以需要注意的是,MPI程序与一般的串行程序应该是完全不一样的,因为相同的代码段会被不同的进程同时运行,而需要程序员在写代码的时候就考虑到不同进程的运行情况,写出特殊的代码,进程与进程之间通过通信子来进行相互通信交换数据。
如果只是简单地将原本的串行程序放上去,那只不过是同时在多个核上跑了很多遍一模一样的代码而已,完全达不到加速的效果。
关键的MPI语句运行完毕之后,如果中间有创建过另外的通信子或者组的话,还需要单独将它们释放掉。
最后,调用MPI_Finalize()
来结束MPI程序。
基本函数
1 | MPI_Comm_size( |
返回comm
通信子中的进程总数。
1 | MPI_Comm_rank( |
返回当前进程在comm
通信子中的序号。
- 点对点通信
1 | MPI_Send( |
向dest
进程发送信息。
1 | MPI_Recv( |
从source
进程接收信息。
1 | MPI_Probe( |
与MPI_Recv()
很像,除了不能接收消息,其他都一样,主要是为了将下一条消息的各项属性保存到status
里面来。
- 集合通信
1 | MPI_Barrier( |
将comm
通信子中的所有进程都同步到当前位置。
1 | MPI_Bcast( |
从source
进程向通信子中的所有其他进程发送消息。
MPI_Bcast()
用的是树形结构的广播,会比单纯的从source
进程开个for循环要高效的多。
然而我在单台电脑上实测的时候貌似没发现
MPI_Bcast()
能快多少…反而有时候还慢很多,不知道是不是我发送的东西太少了,还是电脑本身的处理速度太快了。
1 | MPI_Scatter( |
将source
进程中的send_buf
均分成多份,分发给通信子中的所有进程。
需要注意的是send_count
和recv_count
的值是一样的,都等于每个通信子将收到的数据数量。
1 | MPI_Gather( |
与上面的MPI_Scatter()
非常相似,这个的作用是从多个进程中接收一定数量的数据到dest
的recv_buf
中。
同样send_count
和recv_count
的值是一样的,都等于每个通信子需要发送的数据量。
这里有完整的MPI函数说明。
后话
前段时间忙着准备期末考试,原本准备好的好多个文档都没看,然后之前买的书也都没怎么看。我发现我就是擅长挖坑,然后挖了坑又填不上 o( ̄▽ ̄)d。
虽然看到现在,基本的MPI操作已经明白了,但是还是比较缺少实践。
接下来准备再看下OpenMP和CUDA,然后找点小项目来练练手吧。