MPI編程簡介

第三章 MPI編程

 

3.1 MPI簡介

多線程是一種便捷的模型,其中每個線程都可以訪問其它線程的存儲空間。因此,這種模型只能在共享存儲系統之間移植。一般來講,並行機不一定在各處理器之間共享存儲,當面向非共享存儲系統開發並行程序時,程序的各部分之間通過來回傳遞消息的方式通信。要使得消息傳遞方式可移植,就需要採用標準的消息傳遞庫。這就促成的消息傳遞接口(Message Passing Interface, MPI)的面世,MPI是一種被廣泛採用的消息傳遞標準[1]

OpenMP並行程序不同,MPI是一種基於消息傳遞的並行編程技術。消息傳遞接口是一種編程接口標準,而不是一種具體的編程語言。簡而言之,MPI標準定義了一組具有可移植性的編程接口。各個廠商或組織遵循這些標準實現自己的MPI軟件包,典型的實現包括開放源代碼的MPICHLAM MPI以及不開放源代碼的Intel MPI。由於MPI提供了統一的編程接口,程序員只需要設計好並行算法,使用相應的MPI庫就可以實現基於消息傳遞的並行計算。MPI支持多種操作系統,包括大多數的類UNIXWindows系統。

3.1.1如何實現MPI

MPI是一個標準。它不屬於任何一個廠商,不依賴於某個操作系統,也不是一種並行編程語言。不同的廠商和組織遵循着這個標準推出各自的實現,而不同的實現也會有其不同的特點。MPICH是影響最大、用戶最多的MPI實現。目前可下載的最新的MPICH軟件包爲MPICH1.2.7pl2008215日發佈的MPICH 2-1.0.7測試版(我使用的是MPICH 2-1.0.6pl),在http://www.mcs.anl.gov/research/projects/mpich2/index.php可以下載到,分別有支持UNIXWindows32位和64位版本。

3.1.2 MPI程序的特點

MPI程序是基於消息傳遞的並行程序。消息傳遞指的是並行執行的各個進程具有自己獨立的堆棧和代碼段,作爲互不相關的多個程序獨立執行,進程之間的信息交互完全通過顯示地調用通信函數來完成。

3.2 MPICH的安裝和配置

我使用的MPICH2安裝文件是mpich2-1.0.6p1-win32-ia32.msi,在Windows下安裝MPICH2比較簡單,但是要有Microsoft .NET Framework 2.0的支持。安裝基本上只要單擊“Next”即可。在安裝過程中會提示輸入進程管理器的密碼,這個密碼被用來訪問所有的程序,這裏使用的密碼爲admin

安裝完成後,安裝目錄下的include子目錄包含了編程所需要的所有頭文件,lib子目錄包含了相應的程序庫,而子目錄bin則包含了MPIWindows下面必須的運行程序。運行時需要的動態鏈接庫被安裝在了Windows系統目錄中。在Windows平臺下可以使用Microsoft Visual Studio來開發MPI程序,下面舉例說明。

首先,新建一個Win32控制檯項目,然後將MPICH2安裝目錄下的include

 

圖3-1 配置頭文件目錄

 

子目錄加入到頭文件目錄中。在VS 2005的菜單 工具->選項->項目解決方案->VC++目錄對話框中添加include子目錄,如圖3-1所示。再用相同的方法將MPICH2\lib加入到庫文件目錄中,如圖3-2。

 

圖3-2 配置庫文件目錄

 

爲了避免名字衝突,需要在預編譯頭文件stdafx.h中加入#inlcude mpi.h語句。現在就可以在主程序文件中編寫MPI程序了,MPI的開發環境配置完畢。

3.3 在Windows下如何運行MPI程序

我所進行的MPI程序的開發均是在Windows平臺下,使用Visual Studio 2005 + MPIEXEC wrapper 進行的,首先用一個簡單的Hello World 程序說明運行環境的配置。

按照上一小節介紹配置好開發環境之後,在VS 2005中新建立一個Win32 控制檯項目,並取名MPI1,在MPI1.CPP文件中輸入下面的程序。在項目屬性的“配置屬性”->“常規”項中的“字符集”設置爲“未設置”,如圖3-3所示。

例3_1

int _tmain(int argc, _TCHAR* argv[])

{   int rank, size;

       MPI_Init(&argc, &argv);

       MPI_Comm_rank(MPI_COMM_WORLD, &rank);

       MPI_Comm_size(MPI_COMM_WORLD, &size);

       printf("Hello World from thread %d of %d\n", rank, size);

       MPI_Finalize();

       return 0;

}

這個程序比較簡單,在函數MPI_Init()MPI_Finalize()之間是程序並行執行的地方,MPI_Init()MPI_Comm_rank()MPI_Comm_size()MPI_Finalize(),這四個函數是MPI中最重要和最常用的函數。下面分別說明:

 

圖3-3 配置項目屬性

(1)    MPI_InitMPI_Finalize

MPI_Init用來初始化MPI執行環境,建立多個MPI進程之間的聯繫,爲後續通信做準備。而MPI_Finalize則是結束MPI執行環境。這兩個函數就是定義MPI程序的並行區的,除了檢測是否初始化的函數之外,不應該在這兩個函數定義的區域外調用其它MPI函數。這兩個函數都返回整型值,標識函數是否調用成功。

(2)    MPI_Comm_rank

MPI_Comm_rank函數就是用來標識各個MPI進程的,給出調用該函數的進程的進程號。MPI_Comm_rank返回整型的錯誤值,需要提供兩個參數:

l        MPI_Comm類型的通信域,標識參與計算的MPI進程組。上面例子中使用的是MPI_COMM_WORLD,這個進程組是MPI實現預先定義好的進程組,指的是所有MPI進程所在的進程組。如果想要申請自己的特殊的進程組,則需要通過MPI_Comm定義並通過其它MPI函數生成。

l        &rank返回調用進程中的標識號。

MPI還定義了另一個進程組MPI_COMM_SELF,只包含各個進程自己的進程組。

(3)    MPI_Comm_size

這個函數則用來標識相應進程組中有多少個進程,它也有兩個參數:

l        MPI_Comm類型的通信域,標識參與計算的MPI進程組。上面的例子中用的是MPI_COMM_WORLD

l        &size返回相應進程組中的進程數。

運行這個程序,運行結果如圖3-4,按照並行執行的方式,上面程序運行結果應該打印兩行文字信息,爲:

Hello World from thread 0 of 2

Hello World from thread 1 of 2

 

 

圖 3-4 例3_1在windows上的運行結果

(本機系統環境變量OMP_NUM_THREADS值是2),但是運行結果確只打印了一行,顯然函數MPI_InitMPI_Finalize之間的代碼僅被一個線程串行執行了。經過查詢資料知道,MPI程序若要被正確運行需要使用MPICH2安裝目錄下的運行工具MPIEXEC wrapper運行用VS 2005生成的exe文件。啓動這個程序,程序的界面如圖3-5

 

 

圖 3-5 MPIEXEC wrapper程序界面

由於該程序只有操作系統的管理員纔有權使用,所以在第一次運行時需要輸入計算機用戶名和口令,並且不允許口令爲空,如圖3-6。輸入完畢後,單擊“Register”按鈕完成註冊,之後就可以使用該工具運行MPI程序了。

“Application”欄中選擇要運行的exe程序,在“Number of process”欄中選擇要運行程序的線程數,然後單擊“Execute”按鈕運行程序。如用4線程運行上面的示例程序,輸出結果如圖3-7所示。

 

             圖 3-6 輸入系統用戶名和口令

 

圖 3-7 使用MPIEXEC wrapper運行例3_1的結果

 

4線程分別執行MPI_InitMPI_Finalize之間的代碼,打印4行信息,程序執行結果正確。

3.4 MPI的點對點通信

點對點通信是MPI程序的基礎,MPI_SendMPI_Recv是兩個最重要的函數。這兩個函數的標準形式是:

l        int MPI_Send(buf, counter, datatype, dest, tag, comm)

參數作用如下:

    buf:發送緩衝區的起始地址,可以是數組或結構指針

    count:非負整數,發送的數據個數

    datatype:發送數據的數據類型

    dest:整型,目的的進程號

    tag:整型,消息標誌

    commMPI進程組所在的通信域

這個函數返回整型的錯誤碼,它的含義是向通信域中的dest進程發送數據,數據存放在buf中,類型是datatype,個數是count,這個消息的標誌是tag,用以和本進程向同一目的進程發送的其它消息區別開來。

l        int MPI_Recv(buf, count, datatype, source, tag, comm, status)

參數作用如下:

    buf:接收緩衝區的起始地址,可以是數組或結構指針

    count:非負整數,最多可接收的數據個數

    datatype:接收數據的數據類型

    source:整型,接收數據的來源,即發送數據進程的進程號

    tag:整型,消息標識,應與發送操作的消息標識相同

    comm:消息接收進程所在的通信域

    statusMPI_Status結構指針,返回狀態信息

這個函數返回整型的錯誤碼,它的含義是進程從comm域中source進程接收標籤號爲tag的數據,並保存到buf中。接收緩衝區buf的大小不能小於發送過來的消息的長度。否則會由於數組越界導致程序出錯。參數statusMPI_Status類型的,status主要顯示接收函數的各種錯誤狀態。通過訪問status.MPI_SOURCEstatus.MPI_TAGstatus.MPI_ERROR就可以得到發送數據的進程號、使用的標籤以及接收操作的錯誤代碼。另外,還可以使用函數MPI_Get_count來獲得實際接收到的數據項數。MPI_Get_count的標準定義爲:int MPI_Get_count(MPI_Status *status, MPI_Datatype datatype, int *count);將實際接收到數據項數存放到count中。下面用一個程序說明上面提到的函數的使用方法。

示例程序見例3_2

程序的運行結果如圖3-8(4個進程)

函數MPI_Get_processor_name用於獲得計算機名,並存放在processor_name中,長度爲namelen,宏定義MPI_MAX_PROCESSOR_NAME是機器名的最大長度。這個程序的完成的任務是使進程i發送數據給進程i+1,並等待由進程i-1發送來的數據。最後一個進程則發送數據給進程0。

3.5統計時間函數

爲了驗證程序並行化後的效果,MPI提供了兩個用於統計時間的函數 MPI_WtimeMPI_Wtick。其中MPI_Wtime返回一個雙精度數,表示從過去某點的時刻到當前時刻所消耗的時間秒數。而函數MPI_Wtick則返回MPI_Wtime結果的精度。修改例3_2程序,在並行代碼兩端加入統計時間的函數,如例3_3:

例 3_3(完整程序見示例程序4_3)

       begin = MPI_Wtime();

       end = MPI_Wtime();

       diff = end - begin;

       printf("%d process time is %9.7f\n", myid, diff);

       printf("%d process tick is %9.7f\n", myid, MPI_Wtick());

}

運行結果如圖3-9:

 

 

圖 3-8 例3_2的運行結果

 

 

圖 3-9 例3_3的運行結果

3.6負載均衡對程序性能的影響

在並行計算中,如果各個處理器上的工作所需要的完成時間不同,則會使先完成工作的處理器等待未完成工作的處理器,浪費了計算資源。這時應該使各個處理器的負載儘量均衡。一般採用的策略有兩種:靜態負載平衡和動態負載平衡。前者適用於計算前可以準確知道負載,而且這些負載容易平均劃分給各個進程的情況。而對於事先不知道負載情況,或者總負載不易劃分的情況,則需要採用動態負載劃分來解決。在動態負載平衡模式中存在一個管理結點負責給各個進程分配任務,當一個進程完成當前的計算任務後,它就向管理結點申請新的任務,如果還有未分配的任務,管理結點就將任務分配給那個進程,這有點類似於計算機硬件向CPU發中斷請求服務的方式。

3.7 開發實例

下面將在Windows平臺上使用MPI編寫一個用數值積分法計算圓周率的程序。利用公式PI

 

的近似值計算圓周率[7],定積分的計算可以轉化爲求一個曲邊梯形的面積問題。將積分區間等分成n個小的子區間,可將每個小的子區間上的曲邊梯形近似地看成矩形,這些矩形面積的和就近似地等於原來曲邊梯形的面積。這樣最終將求圓周率的問題轉化成了一個面積迭加的計算。每個小矩形的寬爲

 

(n爲將積分區間等分的份數),高可以將x值帶入函數

 

求得。用循環將每個小矩形的面積累加起來便是PI的近似值。具體的算法實現見附加中的程序“mpi_pi”。圖3-103-11分別是用一個進程和兩個進程運行的結果。

 

3-10 使用一個進程的運行結果

3-11 使用兩個進程的運行結果

從運行結果可以看到使用兩個進程時的計算速度反而不如用一個進程運行時的速度,這時由於本程序的計算規模不大,另外引入一個進程的開銷大於程序並行所帶來的益處,所以進程數越多反而程序的運行速度越慢。看下面一組數據[8](3-1)

計算機數

計算時間

1

1.63643

2

0.83180

3

0.55622

這組數據是在不同的硬件平臺下實現本開發實例程序的計算時間。運行環境爲臺計算機組成的集羣配置均爲CPU : Intel PentiumIII 733MHz,相同的算法,隨着參與計算的機器數增加,計算時間減少。

MPI是針對分佈式計算機系統提出的,它採用非共

3-1                享內存的方式利用多進程完成並行任務,當計算規模不大或處理器數量不多時,更多進程的維護會增加系統的開銷,而且進程之間的通信存在延時。它比較適合集羣計算機系統。

3.8 小結

本章對MPI編程進行了初步研究,介紹了MPI程序的特點、軟件包的安裝、MPI程序的運行方式。

MPI是一種基於消息傳遞的並行編程技術,而不是一種具體的編程語言。MPI程序與OpenMP程序的最大不同就是MPI程序不僅可以適用多線程的方式並行運算還可以讓程序以多進程的方式執行,以這種方式執行的程序並不共享內存,各個進程是通過消息傳遞來進行通信的。這樣做的好處是完成某一計算任務的不同進程可以運行在不同處理器上(不只是處理器的不同核上),甚至是不同的結點計算機上,方便分佈式計算系統的構建。在多核上使用MPI可以採用兩種方式,一種是在多核平臺上開發傳統的多進程MPI並行程序,一個核執行一個MPI進程。另外一種方式是採用MPI + OpenMP的方法,在結點內採用多線程方式,結點間採用MPI多進程方式。


轉自:http://blog.csdn.net/gexplore/article/details/7078832


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章