C++ Boost MPI接口詳細(xì)講解
一、說(shuō)明
Boost.MPI 提供了 MPI 標(biāo)準(zhǔn)(消息傳遞接口)的接口。該標(biāo)準(zhǔn)簡(jiǎn)化了并發(fā)執(zhí)行任務(wù)的程序的開發(fā)。您可以使用線程或通過(guò)共享內(nèi)存或網(wǎng)絡(luò)連接使多個(gè)進(jìn)程相互通信來(lái)開發(fā)此類程序。 MPI 的優(yōu)點(diǎn)是你不需要關(guān)心這些細(xì)節(jié)。您可以完全專注于并行化您的程序。
缺點(diǎn)是您需要 MPI 運(yùn)行時(shí)環(huán)境。如果您控制運(yùn)行時(shí)環(huán)境,MPI 只是一個(gè)選項(xiàng)。例如,如果你想分發(fā)一個(gè)可以通過(guò)雙擊啟動(dòng)的程序,你將無(wú)法使用 MPI。雖然操作系統(tǒng)開箱即用地支持線程、共享內(nèi)存和網(wǎng)絡(luò),但它們通常不提供 MPI 運(yùn)行時(shí)環(huán)境。用戶需要執(zhí)行額外的步驟來(lái)啟動(dòng) MPI 程序。
- 開發(fā)和運(yùn)行時(shí)環(huán)境
- 簡(jiǎn)單的數(shù)據(jù)交換
- 異步數(shù)據(jù)交換
- 集體數(shù)據(jù)交換
二、開發(fā)和運(yùn)行時(shí)環(huán)境
MPI 定義了用于并行計(jì)算的函數(shù)。并行計(jì)算是指在支持任務(wù)并行執(zhí)行的運(yùn)行時(shí)環(huán)境中可以并發(fā)執(zhí)行任務(wù)的程序。這樣的運(yùn)行時(shí)環(huán)境通?;诙鄠€(gè)處理器。由于單個(gè)處理器只能順序執(zhí)行代碼,因此鏈接多個(gè)處理器會(huì)創(chuàng)建一個(gè)可以并行執(zhí)行任務(wù)的運(yùn)行時(shí)環(huán)境。如果連接了數(shù)千個(gè)處理器,結(jié)果就是一臺(tái)并行計(jì)算機(jī)——一種通常只在超級(jí)計(jì)算機(jī)中才能找到的架構(gòu)。 MPI 來(lái)自于尋找更容易地為超級(jí)計(jì)算機(jī)編程的方法的搜索。
如果你想使用 MPI,你需要一個(gè)標(biāo)準(zhǔn)的實(shí)現(xiàn)。雖然 MPI 定義了許多功能,但它們通常不受開箱即用的操作系統(tǒng)支持。例如,Windows 的桌面版本不附帶 MPI 支持。
最重要的 MPI 實(shí)現(xiàn)是 MPICH 和 Open MPI。 MPICH 是最早的 MPI 實(shí)現(xiàn)之一。它自 1990 年代中期就已存在。 MPICH 是一種成熟且可移植的實(shí)現(xiàn),并得到積極維護(hù)和更新。 Open MPI 的第一個(gè)版本于 2005 年發(fā)布。由于 Open MPI 是一項(xiàng)協(xié)作成果,其中包括許多負(fù)責(zé)早期 MPI 實(shí)現(xiàn)的開發(fā)人員,因此 Open MPI 被視為未來(lái)的標(biāo)準(zhǔn)。然而,這并不意味著可以忽略 MPICH。有幾種基于 MPICH 的 MPI 實(shí)現(xiàn)。例如,Microsoft 發(fā)布了一個(gè)名為 Microsoft HPC Pack 的 MPI 實(shí)現(xiàn),它基于 MPICH。
MPICH 為各種操作系統(tǒng)(如 Windows、Linux 和 OS X)提供安裝文件。如果您需要 MPI 實(shí)現(xiàn)并且不想從源代碼構(gòu)建它,MPICH 安裝文件是開始使用 MPI 的最快途徑。
MPICH 安裝文件包含開發(fā) MPI 程序所需的頭文件和庫(kù)。此外,它們還包含一個(gè) MPI 運(yùn)行時(shí)環(huán)境。因?yàn)?MPI 程序同時(shí)在多個(gè)處理器上執(zhí)行任務(wù),所以它們?cè)诙鄠€(gè)進(jìn)程中運(yùn)行。一個(gè) MPI 程序會(huì)啟動(dòng)多次,而不僅僅是一次。同一 MPI 程序的多個(gè)實(shí)例在多個(gè)處理器上運(yùn)行,并通過(guò) MPI 標(biāo)準(zhǔn)定義的函數(shù)進(jìn)行通信。
您無(wú)法通過(guò)雙擊啟動(dòng) MPI 程序。您使用一個(gè)幫助程序,通常稱為 mpiexec。您將 MPI 程序傳遞給 mpiexec,它會(huì)在 MPI 運(yùn)行時(shí)環(huán)境中啟動(dòng)您的程序。命令行選項(xiàng)確定啟動(dòng)了多少個(gè)進(jìn)程以及它們?nèi)绾瓮ㄐ?mdash;—例如,通過(guò)套接字或共享內(nèi)存。因?yàn)?MPI 運(yùn)行時(shí)環(huán)境會(huì)處理這些細(xì)節(jié),所以您可以專注于并行編程。
如果您決定使用 MPICH 的安裝文件,請(qǐng)注意 MPICH 僅提供 64 位版本。您必須使用 64 位編譯器通過(guò) MPICH 開發(fā) MPI 程序并構(gòu)建 64 位版本的 Boost.MPI。
三、簡(jiǎn)單數(shù)據(jù)交換
Boost.MPI 是 MPI 標(biāo)準(zhǔn)的 C++ 接口。該庫(kù)使用命名空間 boost::mpi。包含頭文件 boost/mpi.hpp 就足以訪問(wèn)所有類和函數(shù)。
示例 47.1。 MPI 環(huán)境和通信器
#include <boost/mpi.hpp> #include <iostream> int main(int argc, char *argv[]) { boost::mpi::environment env{argc, argv}; boost::mpi::communicator world; std::cout << world.rank() << ", " << world.size() << '\n'; }
示例 47.1 是一個(gè)簡(jiǎn)單的 MPI 程序。它使用兩個(gè)類,您將在隨后的所有示例中找到它們。 boost::mpi::environment 初始化 MPI。構(gòu)造函數(shù)從 MPI 標(biāo)準(zhǔn)調(diào)用函數(shù) MPI_Init()。析構(gòu)函數(shù)調(diào)用 MPI_Finalize()。 boost::mpi::communicator 用于創(chuàng)建通信器。通信器是 MPI 的核心概念之一,支持進(jìn)程之間的數(shù)據(jù)交換。
boost::mpi::environment 是一個(gè)非常簡(jiǎn)單的類,只提供了幾個(gè)成員函數(shù)。您可以調(diào)用 initialized() 檢查 MPI 是否已成功初始化。成員函數(shù)返回一個(gè) bool 類型的值。 processor_name() 以 std::string 的形式返回當(dāng)前進(jìn)程的名稱。 abort() 會(huì)停止 MPI 程序,而不僅僅是當(dāng)前進(jìn)程。您將一個(gè) int 值傳遞給 abort()。該值將作為 MPI 程序的返回值傳遞到 MPI 運(yùn)行時(shí)環(huán)境。對(duì)于大多數(shù) MPI 程序,您不需要這些成員函數(shù)。您通常在程序開始時(shí)實(shí)例化 boost::mpi::environment,然后不使用該對(duì)象——如示例 47.1 和本章中的以下示例。
boost::mpi::communicator 更有趣。此類是一個(gè)通信器,它鏈接作為 MPI 程序一部分的進(jìn)程。每個(gè)進(jìn)程都有一個(gè)等級(jí),它是一個(gè)整數(shù)——所有進(jìn)程都會(huì)被枚舉。進(jìn)程可以通過(guò)在通信器上調(diào)用 rank() 來(lái)發(fā)現(xiàn)其等級(jí)。如果進(jìn)程想知道有多少個(gè)進(jìn)程,它會(huì)調(diào)用 size()。
要運(yùn)行示例 47.1,您必須使用您正在使用的 MPI 實(shí)現(xiàn)提供的幫助程序。對(duì)于 MPICH,輔 助程序稱為 mpiexec。您可以通過(guò)以下命令使用此幫助程序運(yùn)行示例 47.1:
mpiexec-n4sample.exe
mpiexec 需要一個(gè) MPI 程序的名稱和一個(gè)告訴它要啟動(dòng)多少進(jìn)程的選項(xiàng)。選項(xiàng) -n 4 告訴 mpiexec 啟動(dòng)四個(gè)進(jìn)程。因此 MPI 程序啟動(dòng)了四次。但是,這四個(gè)過(guò)程并不是獨(dú)立的。它們通過(guò) MPI 運(yùn)行時(shí)環(huán)境鏈接,并且它們都屬于同一個(gè)通信器,這給每個(gè)進(jìn)程一個(gè)等級(jí)。如果您使用四個(gè)進(jìn)程運(yùn)行示例 47.1,rank() 返回一個(gè)從 0 到 3 的數(shù)字和 size() 4。
請(qǐng)注意,輸出可能會(huì)混淆。畢竟,有四個(gè)進(jìn)程同時(shí)寫入標(biāo)準(zhǔn)輸出流。例如,不知道排名為 0 或任何其他排名的進(jìn)程是否是第一個(gè)寫入標(biāo)準(zhǔn)輸出流的進(jìn)程。也有可能一個(gè)進(jìn)程在寫入標(biāo)準(zhǔn)輸出流時(shí)會(huì)中斷另一個(gè)進(jìn)程。在另一個(gè)進(jìn)程寫入標(biāo)準(zhǔn)輸出流之前,被中斷的進(jìn)程可能無(wú)法完成寫入其等級(jí)和通信器的大小,從而破壞輸出。
示例 47.2。發(fā)送和接收數(shù)據(jù)的阻塞函數(shù)
#include <boost/mpi.hpp> #include <iostream> int main(int argc, char *argv[]) { boost::mpi::environment env{argc, argv}; boost::mpi::communicator world; if (world.rank() == 0) { int i; world.recv(1, 16, i); std::cout << i << '\n'; } else if (world.rank() == 1) { world.send(0, 16, 99); } }
boost::mpi::communicator 提供了兩個(gè)簡(jiǎn)單的成員函數(shù),send() 和 recv(),用于在兩個(gè)進(jìn)程之間交換數(shù)據(jù)。它們是阻塞函數(shù),僅在發(fā)送或接收數(shù)據(jù)時(shí)才返回。這對(duì)于 recv() 尤其重要。如果在沒有其他進(jìn)程向其發(fā)送數(shù)據(jù)的情況下調(diào)用 recv(),調(diào)用將阻塞并且進(jìn)程將在調(diào)用中停止。
在示例 47.2 中,等級(jí)為 0 的進(jìn)程使用 recv() 接收數(shù)據(jù)。等級(jí)為 1 的進(jìn)程使用 send() 發(fā)送數(shù)據(jù)。如果你用兩個(gè)以上的進(jìn)程啟動(dòng)程序,其他進(jìn)程什么都不做就直接退出。
您將三個(gè)參數(shù)傳遞給 send():第一個(gè)參數(shù)是數(shù)據(jù)應(yīng)發(fā)送到的進(jìn)程的等級(jí)。第二個(gè)參數(shù)是用于識(shí)別數(shù)據(jù)的標(biāo)簽。第三個(gè)參數(shù)是數(shù)據(jù)。
標(biāo)簽始終是一個(gè)整數(shù)。在示例 47.2 中,標(biāo)簽是 16。標(biāo)簽可以識(shí)別對(duì) send() 的調(diào)用。您會(huì)看到該標(biāo)簽與 recv() 一起使用。
傳遞給 send() 的第三個(gè)參數(shù)是 99。這個(gè)數(shù)字從等級(jí) 1 的進(jìn)程發(fā)送到等級(jí) 0 的進(jìn)程。Boost.MPI 支持所有原始類型??梢灾苯影l(fā)送像 99 這樣的 int 值。
recv() 需要類似的參數(shù)。第一個(gè)參數(shù)是應(yīng)該從中接收數(shù)據(jù)的進(jìn)程的等級(jí)。第二個(gè)參數(shù)是將對(duì) recv() 的調(diào)用與對(duì) send() 的調(diào)用鏈接起來(lái)的標(biāo)簽。第三個(gè)參數(shù)是存放接收到的數(shù)據(jù)的變量。
如果您使用至少兩個(gè)進(jìn)程運(yùn)行示例 47.2,則會(huì)顯示 99。
示例 47.3。從任何進(jìn)程接收數(shù)據(jù)
#include <boost/mpi.hpp> #include <iostream> int main(int argc, char *argv[]) { boost::mpi::environment env{argc, argv}; boost::mpi::communicator world; if (world.rank() == 0) { int i; world.recv(boost::mpi::any_source, 16, i); std::cout << i << '\n'; } else { world.send(0, 16, world.rank()); } }
示例 47.3 基于示例 47.2。它不發(fā)送數(shù)字 99,而是發(fā)送調(diào)用 send() 的進(jìn)程的等級(jí)。這可以是等級(jí)大于 0 的任何進(jìn)程。
對(duì)等級(jí)為 0 的進(jìn)程的 recv() 調(diào)用也發(fā)生了變化。 boost::mpi::any_source 是第一個(gè)參數(shù)。這意味著對(duì) recv() 的調(diào)用將接受來(lái)自任何發(fā)送帶有標(biāo)簽 16 的數(shù)據(jù)的進(jìn)程的數(shù)據(jù)。
如果您使用兩個(gè)進(jìn)程啟動(dòng)示例 47.3,將顯示 1。畢竟,只有一個(gè)進(jìn)程可以調(diào)用 send()——rank 為 1 的進(jìn)程。如果你啟動(dòng)程序時(shí)有兩個(gè)以上的進(jìn)程,不知道會(huì)顯示哪個(gè)數(shù)字。在這種情況下,多個(gè)進(jìn)程將調(diào)用 send() 并嘗試發(fā)送它們的排名。哪個(gè)進(jìn)程最先,因此顯示哪個(gè)排名是隨機(jī)的。
示例 47.4。使用 boost::mpi::status 檢測(cè)發(fā)件人
#include <boost/mpi.hpp> #include <iostream> int main(int argc, char *argv[]) { boost::mpi::environment env{argc, argv}; boost::mpi::communicator world; if (world.rank() == 0) { int i; boost::mpi::status s = world.recv(boost::mpi::any_source, 16, i); std::cout << s.source() << ": " << i << '\n'; } else { world.send(0, 16, 99); } }
recv() 的返回值類型為 boost::mpi::status。此類提供了一個(gè)成員函數(shù) source(),它返回從中接收數(shù)據(jù)的進(jìn)程的等級(jí)。示例 47.4 告訴您數(shù)字 99 是從哪個(gè)進(jìn)程收到的。
到目前為止,所有示例都使用 send() 和 recv() 來(lái)傳輸 int 值。在示例 47.5 中,傳輸了一個(gè)字符串。
示例 47.5。使用 send() 和 recv() 傳輸數(shù)組
#include <boost/mpi.hpp> #include <iostream> int main(int argc, char *argv[]) { boost::mpi::environment env{argc, argv}; boost::mpi::communicator world; if (world.rank() == 0) { char buffer[14]; world.recv(boost::mpi::any_source, 16, buffer, 13); buffer[13] = '\0'; std::cout << buffer << '\n'; } else { const char *c = "Hello, world!"; world.send(0, 16, c, 13); } }
send() 和 recv() 可以傳輸數(shù)組和單個(gè)值。示例 47.5 傳輸字符數(shù)組中的字符串。因?yàn)?send() 和 recv() 支持像 char 這樣的基本類型,所以可以毫無(wú)問(wèn)題地傳輸 char 數(shù)組。
send() 將指向字符串的指針作為其第三個(gè)參數(shù),并將字符串的大小作為其第四個(gè)參數(shù)。傳遞給 recv() 的第三個(gè)參數(shù)是指向存儲(chǔ)接收數(shù)據(jù)的數(shù)組的指針。第四個(gè)參數(shù)告訴 recv() 應(yīng)該接收多少個(gè)字符并將其存儲(chǔ)在緩沖區(qū)中。示例 47.5 寫出 Hello, world!到標(biāo)準(zhǔn)輸出流。
示例 47.6。使用 send() 和 recv() 傳輸字符串
#include <boost/mpi.hpp> #include <boost/serialization/string.hpp> #include <string> #include <iostream> int main(int argc, char *argv[]) { boost::mpi::environment env{argc, argv}; boost::mpi::communicator world; if (world.rank() == 0) { std::string s; world.recv(boost::mpi::any_source, 16, s); std::cout << s << '\n'; } else { std::string s = "Hello, world!"; world.send(0, 16, s); } }
盡管 Boost.MPI 只支持原始類型,但這并不意味著它不能傳輸非原始類型的對(duì)象。 Boost.MPI 與 Boost.Serialization 一起工作。可以根據(jù) Boost.Serialization 規(guī)則序列化的對(duì)象可以使用 Boost.MPI 進(jìn)行傳輸。
示例 47.6 傳輸“Hello, world!”這次傳輸?shù)闹挡皇亲址麛?shù)組,而是 std::string。 Boost.Serialization 提供頭文件 boost/serialization/string.hpp,只需要包含它以使 std::string 可序列化。
到此這篇關(guān)于C++ Boost MPI接口詳細(xì)講解的文章就介紹到這了,更多相關(guān)C++ Boost MPI內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
C++段錯(cuò)誤(Segmentation fault)快速定位的解決方法
寫過(guò)C++的朋友都知道,有時(shí)候程序編譯通過(guò),并不能代表程序就是對(duì)的,在linux下做開發(fā)時(shí),經(jīng)常會(huì)遇到跑崩潰的情況,但是在終端只會(huì)報(bào)Segmentation fault,如果工程代碼量少,你還能重新debug一下慢慢找,本文給大家介紹了C++段錯(cuò)誤的快速定位,需要的朋友可以參考下2024-07-07詳解C語(yǔ)言中動(dòng)態(tài)內(nèi)存管理及柔性數(shù)組的使用
這篇文章主要為大家詳細(xì)介紹一下C語(yǔ)言中動(dòng)態(tài)內(nèi)存管理以及柔性數(shù)組的使用方法,文中的示例代碼講解詳細(xì),對(duì)我們學(xué)習(xí)C語(yǔ)言有一定的幫助,需要的可以參考一下2022-07-07c++項(xiàng)目中后綴名vcxproj和sln的區(qū)別及說(shuō)明
這篇文章主要介紹了c++項(xiàng)目中后綴名vcxproj和sln的區(qū)別及說(shuō)明,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-05-05C語(yǔ)言 表、棧和隊(duì)列詳解及實(shí)例代碼
這篇文章主要介紹了C語(yǔ)言 表、棧和隊(duì)列詳解及實(shí)例代碼的相關(guān)資料,需要的朋友可以參考下2017-02-02