網站首頁 編程語言 正文
一、說明
Boost.MPI 提供了 MPI 標準(消息傳遞接口)的接口。該標準簡化了并發執行任務的程序的開發。您可以使用線程或通過共享內存或網絡連接使多個進程相互通信來開發此類程序。 MPI 的優點是你不需要關心這些細節。您可以完全專注于并行化您的程序。
缺點是您需要 MPI 運行時環境。如果您控制運行時環境,MPI 只是一個選項。例如,如果你想分發一個可以通過雙擊啟動的程序,你將無法使用 MPI。雖然操作系統開箱即用地支持線程、共享內存和網絡,但它們通常不提供 MPI 運行時環境。用戶需要執行額外的步驟來啟動 MPI 程序。
- 開發和運行時環境
- 簡單的數據交換
- 異步數據交換
- 集體數據交換
二、開發和運行時環境
MPI 定義了用于并行計算的函數。并行計算是指在支持任務并行執行的運行時環境中可以并發執行任務的程序。這樣的運行時環境通常基于多個處理器。由于單個處理器只能順序執行代碼,因此鏈接多個處理器會創建一個可以并行執行任務的運行時環境。如果連接了數千個處理器,結果就是一臺并行計算機——一種通常只在超級計算機中才能找到的架構。 MPI 來自于尋找更容易地為超級計算機編程的方法的搜索。
如果你想使用 MPI,你需要一個標準的實現。雖然 MPI 定義了許多功能,但它們通常不受開箱即用的操作系統支持。例如,Windows 的桌面版本不附帶 MPI 支持。
最重要的 MPI 實現是 MPICH 和 Open MPI。 MPICH 是最早的 MPI 實現之一。它自 1990 年代中期就已存在。 MPICH 是一種成熟且可移植的實現,并得到積極維護和更新。 Open MPI 的第一個版本于 2005 年發布。由于 Open MPI 是一項協作成果,其中包括許多負責早期 MPI 實現的開發人員,因此 Open MPI 被視為未來的標準。然而,這并不意味著可以忽略 MPICH。有幾種基于 MPICH 的 MPI 實現。例如,Microsoft 發布了一個名為 Microsoft HPC Pack 的 MPI 實現,它基于 MPICH。
MPICH 為各種操作系統(如 Windows、Linux 和 OS X)提供安裝文件。如果您需要 MPI 實現并且不想從源代碼構建它,MPICH 安裝文件是開始使用 MPI 的最快途徑。
MPICH 安裝文件包含開發 MPI 程序所需的頭文件和庫。此外,它們還包含一個 MPI 運行時環境。因為 MPI 程序同時在多個處理器上執行任務,所以它們在多個進程中運行。一個 MPI 程序會啟動多次,而不僅僅是一次。同一 MPI 程序的多個實例在多個處理器上運行,并通過 MPI 標準定義的函數進行通信。
您無法通過雙擊啟動 MPI 程序。您使用一個幫助程序,通常稱為 mpiexec。您將 MPI 程序傳遞給 mpiexec,它會在 MPI 運行時環境中啟動您的程序。命令行選項確定啟動了多少個進程以及它們如何通信——例如,通過套接字或共享內存。因為 MPI 運行時環境會處理這些細節,所以您可以專注于并行編程。
如果您決定使用 MPICH 的安裝文件,請注意 MPICH 僅提供 64 位版本。您必須使用 64 位編譯器通過 MPICH 開發 MPI 程序并構建 64 位版本的 Boost.MPI。
三、簡單數據交換
Boost.MPI 是 MPI 標準的 C++ 接口。該庫使用命名空間 boost::mpi。包含頭文件 boost/mpi.hpp 就足以訪問所有類和函數。
示例 47.1。 MPI 環境和通信器
#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';
}
Example47.1
示例 47.1 是一個簡單的 MPI 程序。它使用兩個類,您將在隨后的所有示例中找到它們。 boost::mpi::environment 初始化 MPI。構造函數從 MPI 標準調用函數 MPI_Init()。析構函數調用 MPI_Finalize()。 boost::mpi::communicator 用于創建通信器。通信器是 MPI 的核心概念之一,支持進程之間的數據交換。
boost::mpi::environment 是一個非常簡單的類,只提供了幾個成員函數。您可以調用 initialized() 檢查 MPI 是否已成功初始化。成員函數返回一個 bool 類型的值。 processor_name() 以 std::string 的形式返回當前進程的名稱。 abort() 會停止 MPI 程序,而不僅僅是當前進程。您將一個 int 值傳遞給 abort()。該值將作為 MPI 程序的返回值傳遞到 MPI 運行時環境。對于大多數 MPI 程序,您不需要這些成員函數。您通常在程序開始時實例化 boost::mpi::environment,然后不使用該對象——如示例 47.1 和本章中的以下示例。
boost::mpi::communicator 更有趣。此類是一個通信器,它鏈接作為 MPI 程序一部分的進程。每個進程都有一個等級,它是一個整數——所有進程都會被枚舉。進程可以通過在通信器上調用 rank() 來發現其等級。如果進程想知道有多少個進程,它會調用 size()。
要運行示例 47.1,您必須使用您正在使用的 MPI 實現提供的幫助程序。對于 MPICH,輔 助程序稱為 mpiexec。您可以通過以下命令使用此幫助程序運行示例 47.1:
mpiexec-n4sample.exe
mpiexec 需要一個 MPI 程序的名稱和一個告訴它要啟動多少進程的選項。選項 -n 4 告訴 mpiexec 啟動四個進程。因此 MPI 程序啟動了四次。但是,這四個過程并不是獨立的。它們通過 MPI 運行時環境鏈接,并且它們都屬于同一個通信器,這給每個進程一個等級。如果您使用四個進程運行示例 47.1,rank() 返回一個從 0 到 3 的數字和 size() 4。
請注意,輸出可能會混淆。畢竟,有四個進程同時寫入標準輸出流。例如,不知道排名為 0 或任何其他排名的進程是否是第一個寫入標準輸出流的進程。也有可能一個進程在寫入標準輸出流時會中斷另一個進程。在另一個進程寫入標準輸出流之前,被中斷的進程可能無法完成寫入其等級和通信器的大小,從而破壞輸出。
示例 47.2。發送和接收數據的阻塞函數
#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 提供了兩個簡單的成員函數,send() 和 recv(),用于在兩個進程之間交換數據。它們是阻塞函數,僅在發送或接收數據時才返回。這對于 recv() 尤其重要。如果在沒有其他進程向其發送數據的情況下調用 recv(),調用將阻塞并且進程將在調用中停止。
在示例 47.2 中,等級為 0 的進程使用 recv() 接收數據。等級為 1 的進程使用 send() 發送數據。如果你用兩個以上的進程啟動程序,其他進程什么都不做就直接退出。
您將三個參數傳遞給 send():第一個參數是數據應發送到的進程的等級。第二個參數是用于識別數據的標簽。第三個參數是數據。
標簽始終是一個整數。在示例 47.2 中,標簽是 16。標簽可以識別對 send() 的調用。您會看到該標簽與 recv() 一起使用。
傳遞給 send() 的第三個參數是 99。這個數字從等級 1 的進程發送到等級 0 的進程。Boost.MPI 支持所有原始類型。可以直接發送像 99 這樣的 int 值。
recv() 需要類似的參數。第一個參數是應該從中接收數據的進程的等級。第二個參數是將對 recv() 的調用與對 send() 的調用鏈接起來的標簽。第三個參數是存放接收到的數據的變量。
如果您使用至少兩個進程運行示例 47.2,則會顯示 99。
示例 47.3。從任何進程接收數據
#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());
}
}
Example47.3
示例 47.3 基于示例 47.2。它不發送數字 99,而是發送調用 send() 的進程的等級。這可以是等級大于 0 的任何進程。
對等級為 0 的進程的 recv() 調用也發生了變化。 boost::mpi::any_source 是第一個參數。這意味著對 recv() 的調用將接受來自任何發送帶有標簽 16 的數據的進程的數據。
如果您使用兩個進程啟動示例 47.3,將顯示 1。畢竟,只有一個進程可以調用 send()——rank 為 1 的進程。如果你啟動程序時有兩個以上的進程,不知道會顯示哪個數字。在這種情況下,多個進程將調用 send() 并嘗試發送它們的排名。哪個進程最先,因此顯示哪個排名是隨機的。
示例 47.4。使用 boost::mpi::status 檢測發件人
#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。此類提供了一個成員函數 source(),它返回從中接收數據的進程的等級。示例 47.4 告訴您數字 99 是從哪個進程收到的。
到目前為止,所有示例都使用 send() 和 recv() 來傳輸 int 值。在示例 47.5 中,傳輸了一個字符串。
示例 47.5。使用 send() 和 recv() 傳輸數組
#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() 可以傳輸數組和單個值。示例 47.5 傳輸字符數組中的字符串。因為 send() 和 recv() 支持像 char 這樣的基本類型,所以可以毫無問題地傳輸 char 數組。
send() 將指向字符串的指針作為其第三個參數,并將字符串的大小作為其第四個參數。傳遞給 recv() 的第三個參數是指向存儲接收數據的數組的指針。第四個參數告訴 recv() 應該接收多少個字符并將其存儲在緩沖區中。示例 47.5 寫出 Hello, world!到標準輸出流。
示例 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 只支持原始類型,但這并不意味著它不能傳輸非原始類型的對象。 Boost.MPI 與 Boost.Serialization 一起工作。可以根據 Boost.Serialization 規則序列化的對象可以使用 Boost.MPI 進行傳輸。
示例 47.6 傳輸“Hello, world!”這次傳輸的值不是字符數組,而是 std::string。 Boost.Serialization 提供頭文件 boost/serialization/string.hpp,只需要包含它以使 std::string 可序列化。
原文鏈接:https://yamagota.blog.csdn.net/article/details/127932153
相關推薦
- 2022-05-15 一起來學習Python的元組和列表_python
- 2022-10-10 Python讀取xlsx文件報錯:xlrd.biffh.XLRDError:?Excel?xlsx?
- 2022-05-23 ELK與Grafana聯合打造可視化監控來分析nginx日志_nginx
- 2022-05-26 Flutter自定義彈窗Dialog效果_Android
- 2022-11-01 使用react在修改state中的數組和對象數據的時候(setState)_React
- 2022-04-04 快應用開發自定義事件 快應用層級 圖片對象Image 獲取元素的寬高
- 2023-03-22 nginx.conf配置兩個前端路徑_nginx
- 2022-08-06 C#使用Tesseract進行Ocr識別的方法實現_C#教程
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支