MPI簡(jiǎn)介
說(shuō)到并行計(jì)算,我們有一個(gè)不可繞開(kāi)的話(huà)題——MPI編程。MPI是一個(gè)跨語(yǔ)言的通訊協(xié)議,用于編寫(xiě)并行計(jì)算機(jī)。支持點(diǎn)對(duì)點(diǎn)和廣播。MPI是一個(gè)信息傳遞應(yīng)用程序接口,包括協(xié)議和和語(yǔ)義說(shuō)明,他們指明其如何在各種實(shí)現(xiàn)中發(fā)揮其特性。MPI的目標(biāo)是高性能,大規(guī)模性,和可移植性。MPI在今天仍為高性能計(jì)算的主要模型。與OpenMP并行程序不同,MPI是一種基于信息傳遞的并行編程技術(shù)。消息傳遞接口是一種編程接口標(biāo)準(zhǔn),而不是一種具體的編程語(yǔ)言。簡(jiǎn)而言之,MPI標(biāo)準(zhǔn)定義了一組具有可移植性的編程接口。
筆者在上一篇文章《如何在win10+vs2013上配置MPI并行編程環(huán)境》中詳細(xì)介紹了如何在win10環(huán)境下配置MPI環(huán)境,還沒(méi)有配置編程環(huán)境的小伙伴建議查看這邊文章,以便于以后的學(xué)習(xí)(畢竟并行機(jī)不是你想擁有就能擁有的)。
MPI基本函數(shù)
MPI調(diào)用借口的總數(shù)雖然龐大, 但根據(jù)實(shí)際編寫(xiě)MPI的經(jīng)驗(yàn), 常用的MPI調(diào)用的個(gè)數(shù)確什么有限。 下面是6個(gè)最基本的MPI函數(shù)。
1.? MPI_Init(…);
2.? MPI_Comm_size(…);
3.? MPI_Comm_rank(…);
4.? MPI_Send(…);
5.? MPI_Recv(…);
6.? MPI_Finalize();
我們?cè)诖送ㄟ^(guò)一個(gè)簡(jiǎn)單的例子來(lái)說(shuō)明這6個(gè)MPI函數(shù)的基本用處。
函數(shù)介紹
1. int MPI_Init (int* argc ,char** argv[] )
該函數(shù)通常應(yīng)該是第一個(gè)被調(diào)用的MPI函數(shù)用于并行環(huán)境初始化,其后面的代碼到 MPI_Finalize()函數(shù)之前的代碼在每個(gè)進(jìn)程中都會(huì)被執(zhí)行一次。
–? 除MPI_Initialized()外, 其余所有的MPI函數(shù)應(yīng)該在其后被調(diào)用。
–? MPI系統(tǒng)將通過(guò)argc,argv得到命令行參數(shù)(也就是說(shuō)main函數(shù)必須帶參數(shù),否則會(huì)出錯(cuò))。
2. int MPI_Finalize (void)
–? 退出MPI系統(tǒng), 所有進(jìn)程正常退出都必須調(diào)用。 表明并行代碼的結(jié)束,結(jié)束除主進(jìn)程外其它進(jìn)程。
–? 串行代碼仍可在主進(jìn)程(rank = 0)上運(yùn)行, 但不能再有MPI函數(shù)(包括MPI_Init())。
3. int MPI_Comm_size (MPI_Comm comm ,int* size )
–? 獲得進(jìn)程個(gè)數(shù) size。
–? 指定一個(gè)通信子,也指定了一組共享該空間的進(jìn)程, 這些進(jìn)程組成該通信子的group(組)。
–? 獲得通信子comm中規(guī)定的group包含的進(jìn)程的數(shù)量。
4. int MPI_Comm_rank (MPI_Comm comm ,int* rank)
–? 得到本進(jìn)程在通信空間中的rank值,即在組中的邏輯編號(hào)(該 rank值為0到p-1間的整數(shù),相當(dāng)于進(jìn)程的ID。)
5. int MPI_Send( void *buff, int count, MPI_Datatype datatype, int dest, int tag, MPI_Comm comm)
–void *buff:你要發(fā)送的變量。
–int count:你發(fā)送的消息的個(gè)數(shù)(注意:不是長(zhǎng)度,例如你要發(fā)送一個(gè)int整數(shù),這里就填寫(xiě)1,如要是發(fā)送“hello”字符串,這里就填寫(xiě)6(C語(yǔ)言中字符串未有一個(gè)結(jié)束符,需要多一位))。
–MPI_Datatype datatype:你要發(fā)送的數(shù)據(jù)類(lèi)型,這里需要用MPI定義的數(shù)據(jù)類(lèi)型,可在網(wǎng)上找到,在此不再羅列。
–int dest:目的地進(jìn)程號(hào),你要發(fā)送給哪個(gè)進(jìn)程,就填寫(xiě)目的進(jìn)程的進(jìn)程號(hào)。
–int tag:消息標(biāo)簽,接收方需要有相同的消息標(biāo)簽才能接收該消息。
–MPI_Comm comm:通訊域。表示你要向哪個(gè)組發(fā)送消息。

6. int MPI_Recv( void *buff, int count, MPI_Datatype datatype, int source, int tag, MPI_Comm comm, MPI_Status *status)
–void *buff:你接收到的消息要保存到哪個(gè)變量里。
–int count:你接收消息的消息的個(gè)數(shù)(注意:不是長(zhǎng)度,例如你要發(fā)送一個(gè)int整數(shù),這里就填寫(xiě)1,如要是發(fā)送“hello”字符串,這里就填寫(xiě)6(C語(yǔ)言中字符串未有一個(gè)結(jié)束符,需要多一位))。它是接收數(shù)據(jù)長(zhǎng)度的上界. 具體接收到的數(shù)據(jù)長(zhǎng)度可通過(guò)調(diào)用MPI_Get_count 函數(shù)得到。
–MPI_Datatype datatype:你要接收的數(shù)據(jù)類(lèi)型,這里需要用MPI定義的數(shù)據(jù)類(lèi)型,可在網(wǎng)上找到,在此不再羅列。
–int dest:接收端進(jìn)程號(hào),你要需要哪個(gè)進(jìn)程接收消息就填寫(xiě)接收進(jìn)程的進(jìn)程號(hào)。
–int tag:消息標(biāo)簽,需要與發(fā)送方的tag值相同的消息標(biāo)簽才能接收該消息。
–MPI_Comm comm:通訊域。
–MPI_Status *status:消息狀態(tài)。接收函數(shù)返回時(shí),將在這個(gè)參數(shù)指示的變量中存放實(shí)際接收消息的狀態(tài)信息,包括消息的源進(jìn)程標(biāo)識(shí),消息標(biāo)簽,包含的數(shù)據(jù)項(xiàng)個(gè)數(shù)等。
示例
基本函數(shù)都已經(jīng)介紹完,現(xiàn)在我們來(lái)用一個(gè)示例來(lái)加強(qiáng)對(duì)這些基本函數(shù)的理解。
#include <stdio.h>
#include <string.h>
#include "mpi.h"
void main(int argc, char* argv[])
{
int numprocs, myid, source;
MPI_Status status;
char message[100];
MPI_Init(&argc, &argv);
MPI_Comm_rank(MPI_COMM_WORLD, &myid);
MPI_Comm_size(MPI_COMM_WORLD, &numprocs);
if (myid != 0) { //非0號(hào)進(jìn)程發(fā)送消息
strcpy(message, "Hello World!");
MPI_Send(message, strlen(message) + 1, MPI_CHAR, 0, 99,
MPI_COMM_WORLD);
}
else { // myid == 0,即0號(hào)進(jìn)程接收消息
for (source = 1; source < numprocs; source++) {
MPI_Recv(message, 100, MPI_CHAR, source, 99,
MPI_COMM_WORLD, &status);
printf("接收到第%d號(hào)進(jìn)程發(fā)送的消息:%s\n", source, message);
}
}
MPI_Finalize();
} /* end main */
運(yùn)行結(jié)果如下圖所示

可以看到,當(dāng)筆者開(kāi)啟四線(xiàn)程運(yùn)行時(shí),1-3號(hào)進(jìn)程發(fā)送消息,0號(hào)進(jìn)程接收到消息并打印;當(dāng)筆者開(kāi)啟八線(xiàn)程運(yùn)行時(shí),1-7號(hào)進(jìn)程發(fā)送消息,0號(hào)進(jìn)程接收到消息并打印。

本文使用的是標(biāo)準(zhǔn)阻塞接收發(fā)送的方式。消息傳遞是MPI的特性,也是我們學(xué)習(xí)的難點(diǎn)。這我們學(xué)習(xí)MPI必須掌握的。
消息發(fā)送與接收函數(shù)的參數(shù)的一些重要說(shuō)明。
1.MPI標(biāo)識(shí)一條消息的信息包含四個(gè)域:
–? 源:發(fā)送進(jìn)程隱式確定,進(jìn)程rank值唯一標(biāo)識(shí).
–? 目的:Send函數(shù)參數(shù)確定.
–? Tag:Send函數(shù)參數(shù)確定, (0,UB) 232-1.
–? 通信子:缺省MPI_COMM_WORLD
?? Group:有限/N, 有序/Rank [0,1,2,…N-1]
?? Contex:Super_tag,用于標(biāo)識(shí)該通訊空間.
2. buffer的使用
buffer必須至少可以容納count個(gè)由datatype指明類(lèi)型的數(shù)據(jù). 如果接收buf太小, 將導(dǎo)致溢出、 出錯(cuò)
3. 消息匹配
–? 參數(shù)匹配source,tag,comm/dest,tag,comm.
–? Source == MPI_ANY_SOURCE: 接收任意處理器來(lái)的數(shù)據(jù)(任意消息來(lái)源).
–? Tag == MPI_ANY_TAG: 匹配任意tag值的消息(任意tag消息).