「并行学习」MPI编程

MPI编程

简介

定义

​ MPI = Message Passing Interface,提供Message Passing方式的API来写程序。也就是说,不需要实际去做一个库(Library)函数,可以直接调用MPI的API来写并行程序。

好处

  1. portable,便携;
  2. Scalable,可扩展的;
  3. Flexible,灵活。

Programming Model

SPMD

​ Single Program Multiple Data。单一的过程中,多个数据或单个程序、多数据计算。
任务是分裂,并同时运行在多个处理器上,以不同的输入更快地获得结果。

在这里插入图片描述

要注意的是:

  1. MPI编程需要一开始就决定处理器的数量,不能在运行过程中添加新的线程(MPI增加了添加新线程的API,但不建议使用),也就是一开始就要决定运行MPI程序时的平行度。

  2. 每个处理器执行的相同的代码,launch到处理器时,会到不同的brach,同时会分配给每个branch不同的ID,然后通过不同的ID读不同的数据的partition(划分),处理不同的数据。

Communication Methods

​ 这里参考了下这篇博客

几个定义

一、从communicated processes的角度

​ 分为Synchronous communication(同步通信)和Asynchronous Communication(异步通信)。

1.Synchronous communication

​ Simultaneously(同步)发送/接受数据。

2.Asynchronous Communication

​ Non-simultaneously(非同步)发送/接受数据。

二、从function calls的角度

​ 分为Blocking(阻塞)和Non-blocking(非阻塞)。

1.Blocking

由于一个function未完成,程序会blocking在一个地方,直到function的完成

​ 阻塞调用是指调用结果返回之前,当前线程会被挂起。调用线程只有在得到结果之后才会返回。

2.Non-blocking

​ 非阻塞调用指在不能立刻得到结果之前,该调用不会阻塞当前线程。

​ 在Linux下,一个socket的文件描述符(file descriptor)默认就是阻塞模式的。在这种模式下,即便这个socket压根没有收到任何数据,我们的read调用也会一直阻塞在那里,无法返回,直到有数据到达为止。

​ 如果我们把这个socket的文件描述符用fcntl设置为非阻塞的。在这种模式下,如果这个socket没有收到任何数据,我们的read调用会立刻返回一个错误。这个时候,我们的程序就知道目前没法从这个socket里读到数据了,索性去干点别的事情,过段时间再调用read当一个应用进程对一个非阻塞的文件描述符循环调用read时,我们称之为轮询(polling)。

​ Blocking calls与synchronous、non-blocking calls 与asynchronous之间不是一一对应的关系,但一般来说,我们会使用blocking calls来实现synchronous,使用non-blocking calls来实现asynchronous。

Message Passing

1.Synchronous/Blocking Message Passing

2.Asynchronous/Non-Blocking Message Passing

​ 与同步数据传输相比,需要加入一个Message Buffer。

​ Message Buffer一般是在sender和/或receiver端之间的一段内存空间。因此两者的通信变为:

​ sender端传递出数据给message buffer,而不是直接给receiver数据。当receiver执行recv()命令时,从message buffer获取数据。

​ 但是需要考虑到的是,如果receiver在sender向message buffer之前执行recv(),此时获取的可能是之前存在message buffer的数据或者是空数据,因此需要在message buffer中添加一个flag/status,receiver执行recv()判断message buffer的状态。

在这里插入图片描述

MPI API

Getting Start

  1. 首先#include mpi.h
  2. MPI calls:

在这里插入图片描述

注意

  1. MPI的函数一般返回的是一个Error Code;因此获取数据的时候不是通过返回值获取,而是通过指针获取的。
  2. 上面的serial code虽然在MPI_INIT()外,但仍然会重复执行。MPI_INIT()所包含的部分只是说会通过message passing interface来进行沟通。
Communicators 和 Groups

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9zQdYKTb-1572696279702)(/Users/panyumingzhi/Documents/asc/MPI编程/MPI_1/截屏2019-10-29下午9.17.40.png)]

一个group有一个communicator,每一个communicator有一个ID。

默认有一个MPI_COMM_WORLD communicator。

Point-to-Point Communication Routines

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XwyY9tam-1572696279705)(/Users/panyumingzhi/Documents/asc/MPI编程/MPI_1/截屏2019-11-01下午7.04.31.png)]

  1. bufferbuffer是一个地址,指到的是要接受或者发送的那个地址(需要注意的是:首先要allocate一个buffer地址,如果不先allocate一个buffer地址会报错,并且allocate的地址空间要大于等于后面 type类型对应的长度 乘以 count的大小得出来的size的值

  2. typecount决定buffer空间的大小。type决定存放的是什么类型的数据,count决定数据元素的数量,type对应的长度乘以count就是size的大小。

  3. type的值为:MPI_CHARMPI_SHORTMPI_INTMPI_LONG等。它们都是define的值,例如MPI_INT可能实际上对应的是数字4,但我们最好不要直接定义该参数为数字4,因为不同电脑int的长度不同,使用MPI_INT更加保险

  4. source/destint类型的值,实际上是sender/receiver的ID。

  5. tagint类型的值,是一个可选项,目的是分传递的数据的类型。发送与接收的tag相同时,才会receiver才会真正的接收message buffer中的数据。如果要接收任意数据,使用MPI_ANY_TAG

  6. requestnon-blocking时使用。在non-blocking中,一个动作不论receiver是否接收到,sender发送到message buffer中之后,就会去做其他的事情。同样,receiver不管是否真的发送完消息,接收到message buffer中的数据(即使是一部分数据)后,都会继续去做计算。但有时,receiver只有在真的接收到sender发送的数据时,才能做下一步的计算。因此,如果需要检查sender是否发送完消息,就需要用到这里的request了。也就是说,request的作用就是检查sender端是否发送完了数据。

  7. status:实际上是一个结构体,记录了receive的讯息,如API call是否成功,从message buffer中获取了多少数据。

blocking的例子
MPI_Comm_rank(MPI_COMM_WORLD, &myRank); /* find process rank */
if (myRank == 0) {
	int x=10;
	MPI_Send(&x, 1, MPI_INT, 1, 0, MPI_COMM_WORLD);
}else if (myRank == 1) {
	int x;
	MPI_Recv(&x, 1, MPI_INT, 0, MPI_ANY_TAG, MPI_COMM_WORLD,status); 
}

这里要注意的是,两个x并不是share-memory的,第一个x位于sender这个process对应的地址空间上,第二个x位于receiver这个process对应的地址空间上。

non-blocking的例子
MPI_Comm_rank(MPI_COMM_WORLD, &myrank);/* find process rank */
if (myrank == 0) {
	int x=10;
	MPI_Isend(&x, 1, MPI_INT, 1, 0, MPI_COMM_WORLD, req1); 
  compute();
} else if (myrank == 1) { 
  int x;
	MPI_Irecv(&x,1,MPI_INT,0,MPI_ANY_TAG,MPI_COMM_WORLD,req1); 
}
MPI_Wait(req1, status);

MPI_Wait()是一个blocking call,程序会卡在这里,直到req1完成,也就是receiver真正接收到了数据。

MPI_Test()返回一个flag,这个flag代表request是否完成。

Collective Communication Routines

Collective Calls

所有collective calls都是blocking calls,一个group中的所有process都需要执行这个collective calls,否则会卡住。

  1. MPI_Barrier(comm):所有属于comm这个group的任务,所有在这个group的process都需要执行MPI_Barrier(comm)这条命令,否则会程序会卡住。

在这里插入图片描述

  1. MPI_Bcast(&buffer,count,datatype,root,comm):将id为root的这个process的buffer中的值broadcast(广播)出去,这条消息将被其他所有process接收到。注意其他所有process的buffer一定要先allocate好,否则将出现问题。
    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-F4Ho6NDB-1572696279706)(/Users/panyumingzhi/Documents/asc/MPI编程/MPI_1/截屏2019-11-01下午8.28.09.png)]
  2. MPI_Scatter(&sendbuf,sendcnt,sendtype,&recvbuf,recvcnt,recvtype,root,comm):
    在这里插入图片描述
    从sender端的值,要分配给receiver端。把buffer中在arrange之内的做均分,分配到每一个receiver端。sendcnt表示了sender端counter的大小,recvcnt表示了receiver端counter的大小。使用这个函数,将会把sender端buffer中的内容平均分配,并给到receiver端。但是,如果sender端只能分3块,而receiver有5个,那么会有两个receiver process不会收到消息,程序仍会继续运行。
  3. MPI_Gather(&sendbuf,sendcnt,sendtype,&recvbuf,recvcnt,recvtype,root,comm):

在这里插入图片描述

从多个send端发送消息,消息汇总到一个receive端中。(与MPI_Scatter()作用相反)

sendcnt:在send buffer中元素的数量;number of elements in send buffer (integer)

recvcnt:一次接收元素的数量。number of elements for any single receive (integer, significant only at root)

  1. MPI_Reduce(&sendbuf,&recvbuf,count,datatype,op,dest,comm)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-asHzVwFZ-1572696279711)(/Users/panyumingzhi/Documents/asc/MPI编程/MPI_1/截屏2019-11-01下午8.59.55.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-f2kVzhfB-1572696279711)(/Users/panyumingzhi/Documents/asc/MPI编程/MPI_1/截屏2019-11-01下午9.01.00.png)]

通过MPI_Reduce()这个函数,将几个process的结果整合到一个process中,并且将数据本身也进行整合。op这个参数,可以选择如何整合:比如,MPI_SUM其他buffer中的结果相加,结果给dest进程的存储在recvbuf中。

  1. MPI_Allgather(&sendbuf,sendcnt,sendtype,&recvbuf,recvcnt,recvtype,root,comm):

在这里插入图片描述

  1. MPI_Allreduce(&sendbuf, &recvbuf, count, datatype, op, comm)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1WWADi96-1572696279712)(/Users/panyumingzhi/Documents/asc/MPI编程/MPI_1/截屏2019-11-02下午6.35.11.png)]

Group and Communicator Routines

先有Group,通过Group创建Communicator

  1. MPI_Comm_group(Comm,&Group),:通过已知的Communicator,获取对应的group的token。
  2. MPI_Group_incl(Group, size, ranks[], &NewGroup):如果要创建新的group,只能从原来的group中选定哪几个作为新group的成员。
  3. MPI_Comm_create(Comm, NewGroup, &NewComm):创建新的communicator。

这里需要注意的是:MPI_Group_incl()和``MPI_Comm_create()参数中,第一个参数GroupComm,是父集的GroupComm`。

上面三个都是Collective Calls!!!

MPI I/O

MPI_File_open()

collective call!!

在这里插入图片描述

使用MPI_File_open()而不是C语言库中的fopen()是因为:如果使用fopen(),多个进程请求同一个文件的时候,会造成错误(系统为了防止同时rewrite的问题,禁止同一个文件同时被打开多次)。

Read & Write

Collective I/O

对小I/O比较好

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vJBJmtnv-1572696279713)(/Users/panyumingzhi/Documents/asc/MPI编程/MPI_1/截屏2019-11-02下午7.29.06.png)]

Independent I/O

对大I/O较好

在这里插入图片描述

MPI-IO API

在这里插入图片描述

小小的吐槽:最近在看台湾人的网课,一直程式程式的,搞得我有点被带偏Orz。打出来的是程序,实际上心里想的是程式哈哈哈,口音真是一个奇怪的东西。*

小小的吐槽:最近在看台湾人的网课,一直程式程式的,搞得我有点被带偏Orz。打出来的是程序,实际上心里想的是程式哈哈哈,口音真是一个奇怪的东西。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值