【RDMA】MPI over InfiniBand, Omni-Path, Ethernet/iWARP, and RoCE 测试结果
MPI over InfiniBand, Omni-Path, Ethernet/iWARP, and RoCE
什么是MPI
1.什么是MPI
Massage Passing Interface:是消息传递函数库的标准规范,由MPI论坛开发。
一种新的库描述,不是一种语言。共有上百个函数调用接口,提供与C和Fortran语言的绑定
MPI是一种标准或规范的代表,而不是特指某一个对它的具体实现
MPI是一种消息传递编程模型,并成为这种编程模型的代表和事实上的标准
2.MPI的特点
MPI有以下的特点:
消息传递式并行程序设计
指用户必须通过显式地发送和接收消息来实现处理机间的数据交换。
在这种并行编程中,每个并行进程均有自己独立的地址空间,相互之间访问不能直接进行,必须通过显式的消息传递来实现。
这种编程方式是大规模并行处理机(MPP)和机群(Cluster)采用的主要编程方式。
并行计算粒度大,特别适合于大规模可扩展并行算法
用户决定问题分解策略、进程间的数据交换策略,在挖掘潜在并行性方面更主动,并行计算粒度大,特别适合于大规模可扩展并行算法
消息传递是当前并行计算领域的一个非常重要的并行程序设计方式
二、MPI的基本函数
MPI调用借口的总数虽然庞大,但根据实际编写MPI的经验,常用的MPI函数是以下6个:
MPI_Init(…);
MPI_Comm_size(…);
MPI_Comm_rank(…);
MPI_Send(…);
MPI_Recv(…);
MPI_Finalize();
原文链接:https://blog.csdn.net/qq_40765537/article/details/106425355
更多见后面章节。
MPI 应用场景:计算密集型
为什么MPI的关注度逐渐下降?https://www.zhihu.com/question/33396326/answer/56567902
mpi属于hpc领域,和现在的hadoop生态本来就不属于一块。
一个技术能否流行起来,主要取决于三点:自身性能,易用性,环境。
性能上,mpi适用于计算密集型,在对计算要求比较高的地方,确实比hadoop/spark用的广,比如机器学习,生物计算。但是属于io密集型就不会有优势,很多大数据任务属于这种情况。
易用性上,mpi无论是编写和运行环境都比hadoop生态圈要求高,这点应该是mpi的短板。
环境上,hadoop生态圈更加完整,从文件系统,sql,流式处理等等,mpi也有,但是不够丰富。环境上,最重要的一点就是需要有大公司推动,有小公司创新的地方。这点上hadoop背靠java生态圈,加上各种应用多,可以讲故事的地方多,容易获得资本市场的关注。
综上看出mpi只有计算密集任务上有优势,其他都是劣势。
不过随着今天计算,存储,网络三者的性价比在变化,他们所代表的两个领域正在发生融合。
并行计算:MPI总结
原文:并行计算:MPI总结_https://blog.csdn.net/qq_40765537/article/details/106425355
一、MPI简介
1.什么是MPI
Massage Passing Interface:是消息传递函数库的标准规范,由MPI论坛开发。
一种新的库描述,不是一种语言。共有上百个函数调用接口,提供与C和Fortran语言的绑定
MPI是一种标准或规范的代表,而不是特指某一个对它的具体实现
MPI是一种消息传递编程模型,并成为这种编程模型的代表和事实上的标准
2.MPI的特点
MPI有以下的特点:
消息传递式并行程序设计
指用户必须通过显式地发送和接收消息来实现处理机间的数据交换。
在这种并行编程中,每个并行进程均有自己独立的地址空间,相互之间访问不能直接进行,必须通过显式的消息传递来实现。
这种编程方式是大规模并行处理机(MPP)和机群(Cluster)采用的主要编程方式。
并行计算粒度大,特别适合于大规模可扩展并行算法
用户决定问题分解策略、进程间的数据交换策略,在挖掘潜在并行性方面更主动,并行计算粒度大,特别适合于大规模可扩展并行算法
消息传递是当前并行计算领域的一个非常重要的并行程序设计方式
二、MPI的基本函数
MPI调用借口的总数虽然庞大,但根据实际编写MPI的经验,常用的MPI函数是以下6个:
MPI_Init(…);
MPI_Comm_size(…);
MPI_Comm_rank(…);
MPI_Send(…);
MPI_Recv(…);
MPI_Finalize();
三、MPI的通信机制
MPI是一种基于消息传递的编程模型,不同进程间通过消息交换数据。
1.MPI点对点通信类型
所谓点对点的通信就是一个进程跟另一个进程的通信,而下面的聚合通信就是一个进程和多个进程的通信。
MPI消息传递过程
(1)标准模式
该模式下MPI有可能先缓冲该消息,也可能直接发送,可理解为直接送信或通过邮局送信。是最常用的发送方式。
由MPI决定是否缓冲消息
没有足够的系统缓冲区时或出于性能的考虑,MPI可能进行直接拷贝:仅当相应的接收完成后,发送语句才能返回。
这里的系统缓冲区是指由MPI系统管理的缓冲区。而非进程管理的缓冲区。
MPI环境定义有三种缓冲区:应用缓冲区、系统缓冲区、用户向系统注册的通信用缓冲区
MPI缓冲消息:发送语句在相应的接收语句完成前返回。
这时后发送的结束或称发送的完成== 消息已从发送方发出,而不是滞留在发送方的系统缓冲区中。
该模式发送操作的成功与否依赖于接收操作,我们称之为非本地的,即发送操作的成功与否跟本地没关系。
(2)缓冲模式
可理解为通过邮局送信(应用缓冲区)。注意这里是应用缓冲区,和系统缓冲区不同。需要用户程序事先申请一块足够大的缓冲区,用户定义的缓冲区只能用于缓存模式,一个进程一次只能绑定一块用户缓冲区,通过MPI_Buffer_attch实现
发送是本地的: 完成不依赖于与其匹配的接收操作。发送的结束仅表明消息进入用户指定的缓冲区中。
通过MPI_Buffer_detach来回收申请的缓冲区,阻塞模式下该操作直到缓存区的数据传输完才返回。
缓冲模式在相匹配的接收未开始的情况下,总是将送出的消息放在缓冲区内,这样发送者可以很快地继续计算,然后由系统处理放在缓冲区中的消息。
占用额外内存,一次内存拷贝。
其函数调用形式为:MPI_BSEND(…)。B代表缓冲.
(3)同步模式
可理解为握手后才送出名片
本质特征:收方接收该消息的缓冲区已准备好,不需要附加的系统缓冲区
任意发出:发送请求可以不依赖于收方的匹配的接收请求而任意发出
成功结束:仅当收方已发出接收该消息的请求后才成功返回,否则将阻塞。
非本地的
可用于实现进程同步
其函数调用形式为:MPI_SSEND(…)。S代表同步
(4)就绪模式
可理解为有客户请求,才提供服务。
发送请求仅当有匹配的接收后才能发出,否则出错。在就绪模式下,系统默认与其相匹配的接收已经调用。接收必须先于发送。
它依赖于接收方的匹配的接收请求,不可以任意发出。
其函数调用形式为:MPI_RSEND(…)。R代表就绪 。
正常情况下可用标准模式替换,除可能影响性能外,不影响结果。
(5)点对点通信的阻塞性分析
上面的四种方式都有阻塞通信和非阻塞通信的两种版本
阻塞通信
就是按照上面的流程进程自己要等待指定的操作实际完成,或者所涉及的数据被MPI系统安全备份后,函数才返回后才能进行下一步的操作。
非阻塞通信
通信函数总是立即返回,实际操作由MPI后台进行,需要调用其它函数来查询通信是否完成,通信与计算可重叠。
因为阻塞通信时保证了数据的安全性,所以通信还是返回后,其他的函数或者语句能够对这些数据资源直接访问。
但是非阻塞通信函数立即返回,不能保证数据已经被传送出去或者被备份或者已经读入使用,所以即使你没有阻塞,后面的语句可以继续执行,如果你要操纵上面所说的数据,将会导致发送或接收产生错误。所以对于非阻塞操作,要先调用等待MPI_Wait()或测试MPI_Test()函数来结束或判断该请求,然后再向缓冲区中写入新内容或读取新内容。
所以非阻塞性的通信函数一般时用到后面的语句跟本函数数据无冲突的场景,可以提高效率。
如何使用两种版本?
发送语句的前缀由MPI_改为MPI_I, I指的是immediate,即可改为非阻塞,否则是阻塞。示例如下:
标准模式:MPI_Send(…)->MPI_Isend(…)
Buffer模式:MPI_Bsend(…)->MPI_Ibsend(…)
(6) 通信函数的返回和通信函数的完成、和通信的完成
通信的完成=发送函数的完成+接收函数的完成
在阻塞状态下通信函数的返回是在通信即将完成或者完成之后(根据不同的模式有不同设置,如缓冲模式发送函数的返回是在缓冲区的数据传输完之后返回)。在非阻塞状态下,通信函数立即返回。
下面讲讲通信函数的完成:
发送的完成: 代表发送缓冲区中的数据已送出,发送缓冲区可以重用。它并不代表数据已被接收方接收。数据有可能被缓冲;
这里有一个特例,就是同步模式,它的发送完成==接收方已初始化接收,数据将被接收方接收。
接收的完成:代表数据已经写入接收缓冲区。接收者可访问接收缓冲区,status对象已被释放。它并不代表相应的发送操作已结束(即操作函数可返回)。
通信函数的返回和通信函数完成是不一样的。
通过MPI_Wait()和MPI_Test()来判断通信是否已经完成;
(7) 非阻塞发送和接收
int MPI_Isend(void* buf, int count, MPI_Datatype datatype, int dest, int tag,
MPI_Comm comm, MPI_Request *request)
IN buf 发送缓冲区的起始地址
IN count 发送缓冲区的大小(发送元素个数)
IN datatype 发送缓冲区数据的数据类型
IN dest 目的进程的rank值
IN tag 消息标签
IN comm 通信空间/通信域
OUT request 非阻塞通信完成对象(句柄)
MPI_Ibsend/MPI_Issend/MPI_Irsend:非阻塞缓冲模式/非阻塞同步模式/非阻塞就绪模式
int MPI_Irecv(void* buf, int count, MPI_Datatype datatype, int source, int tag,
MPI_Comm comm, MPI_Request* request)
(8)消息探测
MPI_Probe()和MPI_Iprobe()函数探测接收消息的内容。用户根据探测到的消息内容决定如何接收这些消息,如根据消息大小分配缓冲区等。前者为阻塞方式,即只有探测到匹配的消息才返回;后者为非阻塞,即无论探测到与否均立即返回.
int MPI_Probe(int source, int tag, MPI_Comm comm, MPI_Status* status)
int MPI_Iprobe(int source, int tag, MPI_Comm comm, int*flag, MPI_Status* status)
IN source 数据源的rank,可以是MPI_ANY_SOURCE
IN tag 数据标签,可以是MPI_ANY_TAG
IN comm 通信空间/通信域
OUT flag 布尔值,表示探测到与否(只用于非阻塞方式)
OUT status status对象,包含探测到消息的内容
7
(8)通信检查
int MPI_Wait(MPI_Request* request, MPI_Status * status)
通信检测函数:必须等待指定的通信请求完成后才能返回,成功返回时,
status 中包含关于所完成的通信的消息,相应的通信请求被释放,即request 被置成MPI_REQUEST_NULL
int MPI_Test(MPI_Request *request,int *flag, MPI_Status *status
通信检测函数:不论通信是否完成都立刻返回,flag为1表示通信完成
其他通信检测函数
int MPI_Waitany(int count,MPI_Request *array_of_requests,int *index, MPI_Status *status)
int MPI_Waitall(int count,MPI_Request *array_of_requests,MPI_Status *array_of_statuses)
int MPI_Waitsome(int incount,MPI_Request *array_of_requests,int *outcount,
int *array_of_indices,MPI_Status *array_of_statuses)
int MPI_Testany(int count,MPI_Request *array_of_requests,int *index, int *flag,MPI_Status *status)
int MPI_Testall(int count,MPI_Request *array_of_requests,int *flag,MPI_Status *array_of_statuses)
int MPI_Testsome(int incount,MPI_Request *array_of_requests,int *outcount,
int *array_of_indices,MPI_Status *array_of_statuses)
(9)请求释放和请求撤销
MPI_Request_free(MPI_Request request)
释放指定的通信请求(及所占用的内存资源)
若该通信请求相关联的通信操作尚未完成,则等待通信的完成,因此通信请求的释放并不影响该通信的完成
该函数成功返回后request 被置为MPI_REQUEST_NULL
一旦执行了释放操作,该通信请求就无法再通过其它任何的调用访问
MPI_Cancel(MPI_Request request)
非阻塞型,用于取消一个尚未完成的通信请求
在MPI系统中设置一个取消该通信请求的标志后立即返回,具体的取消操作由MPI系统在后台完成。
MPI_CANCEL允许取消已调用的通信请求,但并不意味着相应的通信一定会被取消:若相应的通信请求已经开始,则它会正常完成,不受取消操作的影响;若相应的通信请求还没开始,则可以释放通信占用的资源。
调用MPI_CANCEL 后,仍需用MPI_WAIT,MPI_TEST或MPI_REQUEST_FREE 来释放该通信请求。
MPI_Test_cancelled(MPI_Status status,int *flag)
检测通信请求是否被取消
(9)MPI点对点通信的安全性
进程死锁
不安全的通信
这里之所以会不安全,是因为两个进程都需要buffer。但仅仅只有两个进程时,程序不会发生问题,因为系统缓冲区够用,但是当有100000个进程的时候就会出现死锁。比如现在A,B是刚刚进入的线程,前面的进程使得系统缓冲区已经满了,A,B都被阻塞,因此C、D也会被阻塞,这两个程序就会陷入死锁。
安全的通信
2.MPI聚合通信
上面是点对点通信,聚合通信就是包括了一对多,多对一和多对多的通信方式。
(1) MPI_Bcast
函数原型
int MPI_Bcast (
void buffer, /* 发送/接收buf/
int count, /*元素个数*/
MPI_Datatype datatype,
int root, /*指定根进程*/
MPI_Comm comm)
根进程既是发送缓冲区也是接收缓冲区
示例:
int p, myrank;
float buf;
MPI_Comm comm;
MPI_Init(&argc, &argv);
/*得进程编号*/
MPI_Comm_rank(comm, &my_rank);
/* 得进程总数 */
MPI_Comm_size(comm, &p);
if(myrank==0)
buf = 1.0;
MPI_Bcast(&buf,1,MPI_FLOAT,0, comm);
(2) MPI_Gather
Root进程从n个进程的每一个接收各个进程(包括它自已)的消息. 这n个消息的连接按序号rank进行, 存放在Root进程的接收缓冲中.
int MPI_Gather (
void *sendbuf,
int sendcnt,
MPI_Datatype sendtype,
void *recvbuf,
int recvcount,
MPI_Datatype recvtype,
int root,
MPI_Comm comm )
int p, myrank;
float data[10];/*分布变量*/
float* buf;
MPI_Comm comm;
MPI_Init(&argc, &argv);
/*得进程编号*/
MPI_Comm_rank(comm,&my_rank);
/* 得进程总数 */
MPI_Comm_size(comm, &p);
if(myrank==0)
buf=(float*)malloc(p*10*sizeof(float);/*开辟接收缓冲区*/
MPI_Gather(data,10,MPI_FLOAT,
buf,10,MPI_FlOAT,0,comm);
想要了解更多函数的用法,可以到官方文档查看。
同时如果想要练习这些函数的用法,可以到超算习堂进行练习。
————————————————
版权声明:本文为CSDN博主「JacksonKim」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请
原文链接:https://blog.csdn.net/qq_40765537/article/details/106425355