MPI編程簡單介紹

第三章 MPI編程

 

3.1 MPI簡單介紹

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

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程序是基於消息傳遞的並行程序。消息傳遞指的是並行運行的各個進程具備本身獨立的堆棧和代碼段,做爲互不相關的多個程序獨立運行,進程之間的信息交互全然經過顯示地調用通訊函數來完畢。windows

3.2 MPICH的安裝和配置

我使用的MPICH2安裝文件是mpich2-1.0.6p1-win32-ia32.msi,在Windows下安裝MPICH2比較簡單,但是要有Microsoft .NET Framework 2.0的支持。安裝基本上僅僅要單擊「Next」就能夠。在安裝過程當中會提示輸入進程管理器的password,這個password被用來訪問所有的程序,這裏使用的password爲admin數組

安裝完畢後,安裝文件夾下的include子文件夾包括了編程所需要的所有頭文件,lib子文件夾包括了對應的程序庫,而子文件夾bin則包括了MPIWindows如下必須的執行程序。執行時需要的動態連接庫被安裝在了Windows系統文件夾中。在Windows平臺下可以使用Microsoft Visual Studio來開發MPI程序,如下舉例說明。多線程

首先,新建一個Win32控制檯項目,而後將MPICH2安裝文件夾下的includeapp

 

圖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程序界面

由於該程序僅僅有操做系統的管理員纔有權使用,因此在第一次執行時需要輸入計算機username和口令,並且不一樣意口令爲空,如圖3-6。輸入完畢後,單擊「Register」button完畢註冊,以後就可以使用該工具執行MPI程序了。

「Application」欄中選擇要執行的exe程序,在「Number of process」欄中選擇要執行程序的線程數,而後單擊「Execute」button執行程序。如用4線程執行上面的演示樣例程序,輸出結果如圖3-7所看到的。

 

             圖 3-6 輸入系統username和口令

 

圖 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負載均衡對程序性能的影響

在並行計算中,假設各個處理器上的工做所需要的完畢時間不一樣,則會使先完畢工做的處理器等待未完畢工做的處理器,浪費了計算資源。這時應該使各個處理器的負載儘可能均衡。通常採用的策略有兩種:靜態負載平衡和動態負載平衡。前者適用於計算前可以準確知道負載,而且這些負載easy平均劃分給各個進程的狀況。而對於事先不知道負載狀況,或者總負載不易劃分的狀況,則需要採用動態負載劃分來解決。在動態負載平衡模式中存在一個管理結點負責給各個進程分配任務,當一個進程完畢當前的計算任務後,它就向管理結點申請新的任務,假設還有未分配的任務,管理結點就將任務分配給那個進程,這有點相似於計算機硬件向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

相關文章
相關標籤/搜索