并行计算总结
作者:ArimaMisaki
目錄
1 并行計算概述. 2
1.1 基本概念. 2
1.2 存儲器的層次結構. 3
1.3 并行計算. 3
1.4 動態互連網絡. 4
1.5 并行計算機結構模型. 5
1.6 并行算法的基本設計策略. 6
1.7 并行編程風范. 6
1.8 單核多線程和并發執行. 7
1.9 拓展:并行計算機的分類. 7
1.10 并行層次和代碼粒度. 10
1.11 并行程序設計模型. 10
2并行計算模型. 11
2.1 拓展:進程. 11
2.2 拓展:進程模型. 11
2.3 拓展:父子進程. 12
2.4 拓展:線程. 12
2.5 拓展:用戶線程和內核線程. 12
2.6 POSIX線程. 13
2.7 并行算法. 13
2.8 并行計算模型. 14
2.9 并行算法一般設計過程. 15
2.10 程序性能評價與優化. 15
3 OpenMP并行編程模型. 16
3.1 OpenMP概述. 16
3.2 OpenMP語句模式. 16
3.3 OpenMP簡單演示. 17
3.4 Schedule關于for循環的調度. 18
3.5 設置環境變量(拓展). 19
3.6 sections制導指令. 20
3.7 single制導指令(拓展). 20
3.8 共享任務結構. 21
3.9 OpenMP的優點和缺點. 21
3.10 常用子句的補充. 22
4 MPI并行編程模型. 22
4.1 拓展:什么是MPI 22
4.2 MPI基本函數. 22
4.3 消息傳遞的特點. 24
1 并行計算概述
1.1 基本概念
并行計算:并行計算機或分布式計算機(包括網絡計算機)等高性能計算機系統上所做的超級計算
計算科學:用強大的計算能力去理解和解決復雜問題,是確??茖W領先地位、經濟競爭力和國防安全等的關鍵之所在。科學發現的第三支柱。
計算思維:運用計算機科學的基礎概念進行問題求解、系統設計以及人類行為的理解,是21世紀中葉所有人的一種基本技能,就像現今人們掌握閱讀、寫作和算術技能一樣,希望每個人都能像計算機科學家那樣思考問題。
流水線:通常一條指令執行分為不同的階段(如取指、譯碼、取數、執行等),通過重疊指令執行中的不同階段,可以加快指令的執行速度。
超標量:如果CPU種設有多條流水線,即能同時發射多條指令,這種具有可在同一時鐘周期內發射多條指令功能的處理器就成為超標量。
摩爾定律:“摩爾定律是由英特爾(Intel)創始人之一戈登·摩爾(Gordon Moore)提出來的。其內容為:當價格不變時,集成電路上可容納的元器件的數目,約每隔18-24個月便會增加一倍,性能也將提升一倍。換言之,每一美元所能買到的電腦性能,將每隔18-24個月翻一倍以上。這一定律揭示了信息技術進步的速度。”
多核處理器:AMD和Intel在2005年相繼推出了各自的雙核處理器Opteron和Core Duo
| 拓展:多核處理器 多核CPU芯片可以看做是一個大芯片里面套了好幾個小芯片,每個小芯片都可以看做是一個獨立的CPU,對于Intel Xeon Phi(英特爾至強融核處理器)來說,其上面甚至集成了60多個核。 雖然多核CPU很牛,但是說到多核,可能沒有比GPU更牛的了,GPU指的是成千上萬個微核組成的處理器,其適用于大量的并行簡單計算,還有圖像處理,但是其不太適應串行任務,而且對于編程及算法的實現難度過高,所以一般操作系統還是運行在CPU上比較好。 |
1.2 存儲器的層次結構
| 拓展:計算機中的存儲器結構 這實際上是計算機組成原理的知識。 存儲器一般分為多種,如主存、緩存、輔存、寄存器等等。這里圖中出現了Cache。Cache一般指的是高速緩存,在以前Cache一般位于CPU之外,而在現在大多數Cache都在CPU內部。 Cache存儲器(電腦中為高速緩沖存儲器),是位于CPU和主存儲器DRAM(Dynamic Random Access Memory)之間,規模較小,但速度很高的存儲器,通常由SRAM(Static Random Access Memory)靜態存儲器組成。它是位于CPU與內存間的一種容量較小但速度很高的存儲器。CPU的速度遠高于內存,當CPU直接從內存中存取數據時要等待一定時間周期,而Cache則可以保存CPU剛用過或循環使用的一部分數據,如果CPU需要再次使用該部分數據時可從Cache中直接調用,這樣就避免了重復存取數據,減少了CPU的等待時間,因而提高了系統的效率。Cache又分為L1Cache(一級緩存)和L2Cache(二級緩存),L1Cache主要是集成在CPU內部,而L2Cache集成在主板上或是CPU上。 主存一般指的是內存、寄存器,而輔存一般指外存(如磁盤、CD-ROM也就是光盤等)。各個存儲器之間用總線進行通信,確保數據能夠從計算機的一個位置傳輸到另外一個位置。 寄存器一般處于CPU內部,用來存放數據。對于常用的數據,一般先放在存儲器,放不下了就放到高速緩沖去,再放不下就轉移到磁盤。 |
1.3 并行計算
并行計算的初衷,是為了努力仿真自然世界中一個序列中含有眾多同時發生的、復雜且相關事件的事務狀態。
為了利用并行計算求解一個計算問題,通?;谝韵驴紤]:
并行計算基本上可以分為:
1.4 動態互連網絡
動態網絡是用交換開關構成的,可按應用程序的要求動態地改變連接組態;典型的動態網絡包括總線、交叉開關和多級互連網絡等。
這種網絡比較普遍的是總線上面掛交換器。我們知道同一時間段中,一條總線只允許兩頭的設備進行信息交換,而在交換完成后,交換器可以將總線的端口改變,使其連接另外一個設備。通過這種方法,可以根據我們應用的需求,動態地選擇我們需要的設備。
典型的動態網絡區別如下:
| 拓展:并行計算機系統互連 不同帶寬和距離的互連技術有多種,比較常用是:廣域網WAN、城域網MAN、局域網WAN、個人區域網PAN、總線。廣域網一般跨國,城域網一般城市,局域網一般一棟樓,個人區域網一般幾臺設備。其中廣域網使用了交換技術,而局域網使用的是廣播技術。如果是使用總線的話,總線是最快的,你可以理解為總結傳輸時不需要網絡,直接用一條USB連接的那種。 靜態互聯網絡是處理單元間有著固定連接的一類網絡,在程序執行期間,這種點到點的連接保持不變;典型靜態網絡有一維線性陣列、二維網孔、樹連接、超立方網絡、立方環、洗牌交換網、蝶狀網絡等。 換而言之,靜態互連網絡就是用一個鏈路把多個處理器連接起來,構成物理意義上的并行計算機,如果某個處理器想發信息給另外一個處理器,總是能通過這條鏈路來干這種事。 相對地,我們還有動態互聯網絡。 互聯網絡中還有另外一個概念叫嵌入。其做法是將網絡中的各節點映射到另一個網絡中去。用膨脹系數來描述嵌入的質量,它是指被嵌入網絡中的一條鏈路在所要嵌入的網絡中對應所需的最大鏈路數。如果該系數為1,則稱為完美嵌入。 對于環網和超立方來說,兩者皆可被完美嵌入到2D環繞網中。 |
1.5 并行計算機結構模型
PVP
PVP也叫并行向量處理機(Parallel Vector Processor),其內部含有為數不多、功能強大的定制向量處理器,以及定制的高帶寬縱橫交叉開關和高速數據訪問。其價格十分昂貴,因為其組件都需定制,一般適用于國家部門。
SMP
SMP也叫對稱多處理機。其訪存、IO都是對稱的。其用的處理器大多數是商用處理器。
目前SMP需要解決的主要問題是Cache的一致性問題。多級高速緩存可以支持數據的局部性,而其一致性可由硬件來增強。大多數SMP系統都是基于總線連接的,占據了并行計算機市場中很大的份額。
MMP
MMP也叫`大規模并行處理機(Massively Parallel Processor)`,其規模大,性能好。
DSM
DSM又叫分布式共享存儲器(Distributed Shared Memory,DSM)。在DSM中,每個節點都有本地內存,所有的節點都有一個共享空間。
COW
COW又叫工作站機群(Cluster of Workstation)。工作站機群的結構技術起點比較低,可以自己將一些服務器/微型機通過以太網連起來,加上相應的管理和通訊軟件來搭建自己的工作站機群。
在集群中,每個節點都有本地磁盤,除了沒有顯示器沒有鼠標沒有鍵盤之外,基本上其他普通計算機該有的它都有。每個節點用I/O總線連向專門設計的多級高速網絡。
機群也是構建并行計算機一種很廉價的方案,其被稱為窮人的解決方案。使用這類并行計算機跑并行程序效率很低,但是由于它的性價比和搭建的簡便性,使得近幾年常被用于做并行科學計算和并行商用計算。
需要注意的是,機群不適合用于國家級的計算,因為由上述可知,實際上機群可以理解為是很多廉價的機器并在一起,而如果要運行速度跟快,能處理的數據更多,就需要并一個很大的機群。而如果機群并得很大,就會導致散熱有問題。我們前面說過它們通過總線互聯的,你總不能一個計算機在東一個計算機在西,然后一條總線連著吧??隙ㄊ墙y一放在一個地方啊。而如果要處理大型的數據,一般機群所處的機房就要三四層樓那么高,籃球場那么寬,肯定不利于散熱。
1.6 并行算法的基本設計策略
串行算法的直接并行化;如快排的自然并行化
從問題描述開始設計并行算法;如并行串匹配算法
借用已有算法求解新問題;如使用矩陣乘法算法求解所有點對間最短路徑是一個很好的范例
并行算法常用設計技術
| 注意:分治法和劃分法的區別 分治法的側重點在于子問題的歸并上,而劃分法的注意力則集中在原問題的劃分上,分治是遞歸方式的劃分,它將問題分成子問題后不立即求解它,而是連續地再將其分為更小的、易于求解的子問題。 |
1.7 并行編程風范
并行編程風范是指在并行機上編程實現并行算法的方法。如:
1.8 單核多線程和并發執行
并發執行是指多個線程在同一硬件資源上或單處理器核上交替地執行,在某個特定的時間點,所有活動的線程只有一個在真正執行,但在某段時間間隔內對外表現為多個線程在同時執行。
這種做法并非真正意義上的并行多線程,在單核結構上的應用程序主要靠隱藏延遲的方法來提高應用程序的性能。
影響多線程性能的常見問題有如下幾點:
線程過多、數據競爭、死鎖、Cache偽共享/Cache行乒乓現象。
| 注意:并行和并發 對于學過操作系統的都知道,比較容易混淆的就是并行的概念,我們所說的并行通常指的是:指兩個或多個事件在同一時刻同時發生。 我們用一個例子來說明:有兩個人一個叫小明一個叫小剛。它們每人都有兩個女朋友。對于小明來說,他喜歡的是和一號、二號一起出門約會;而對于小剛來說,他喜歡8:00和一號約會,9:00和二號約會,10:00和一號約會。 這里我們發現兩個人同樣都是在約會,但是小明是同一時刻同時發生,屬于并行;而小剛如果別人問他你怎么約會的,他會說他和兩個女生同時約會,但是實際上,它是和兩個女生交替約會,這就是宏觀和微觀的區別,其屬于并發。 一個單核處理器(CPU)同一時刻只能執行一個程序,因此操作系統會負責協調多個程序交替執行,這就是操作系統的并發性。但是需要注意的是我們強調的是單核處理器,如今的電腦一般都是多核CPU,如Intel的第八代i3處理器就是4核CPU,這意味著同一時刻可以有4個程序并行執行,但是操作系統的并發性依然必不可少,因為每個人根本不可能說一臺電腦只開四個應用程序吧。 |
1.9 拓展:并行計算機的分類
一臺并行計算機可以是一臺具有多個內部處理器的單計算機,也可以是多個互聯的計算機構成一個一體的高性能計算平臺。術語并行計算機通常是指專門設計的部件。根據不同的分類法可以分成不同類型的并行計算機。
1.9.1 費林分類法
在操作系統中我們知道,程序根據高級程序設計語言設計,程序設計語言在實現程序的功能的時候,是轉換為機器指令來告訴機器該干什么。大概在50年前Flynn(1996)創造了一種計算機分類方法,中文譯為費林分類法,該分類基于兩個獨立維度的計算機體系結構,這兩個維度即數據和指令。根據以上提到這兩個維度,我們可以劃分為四大類,如圖:
Single Instruction,Single Data(SISD)
SISD機器是一種傳統的串行計算機,它的硬件不支持任何形式的并行計算,所有的指令都是串行執行。并且在某個時鐘周期(時間片)內,CPU只能處理一個數據流。因此這種機器被稱作單指令流單數據流計算機。早期的計算機都是SISD機器,如馮諾.依曼架構,如IBM PC機,早期的巨型機和許多8位的家用機等。
Multiple Instruction,Multiple Data(MIMD)
在一個通用的多處理機系統中,每個處理器擁有一個獨立的程序,由每個程序為每個處理器生成一個指令流,不同的數據可能需要不同的處理,對應賦給不同的指令。每條指令對不同數據進行操作。Flynn將這種形式的計算機分類為多指令流多數據流計算機。
我們后面敘述的共享存儲器或消息傳遞多處理機都屬于MIMD類型。其已經經受了時間考驗,至今仍然廣泛地用于這種操作模式下的計算機系統中。例如多核CPU計算機。
Single Instruction,Multiple Data(SIMD)
如果對某些應用而言將計算機設計成由單一程序生成指令流,但是卻有多個數據存在時,將會在性能上有很大的優勢。打個比方,你輸入一條指令就能夠處理很多的數據,那不就是提高了性能嗎。我們熟知的Hadoop就是基于SIMD的。
SIMD是采用一個指令流處理多個數據流。這類機器在數字信號處理、圖像處理、以及多媒體信息處理等領域非常有效。
Intel處理器實現的MMXTM、SSE(Streaming SIMD Extensions)、SSE2及SSE3擴展指令集,都能在單個時鐘周期內處理多個數據單元。也就是說我們現在用的單核計算機基本上都屬于SIMD機器。
Multiple Instruction,Single Data(MISD)
MISD是采用多個指令流來處理單個數據流。由于實際情況中,采用多指令流處理多數據流才是更有效的方法,誰會去一個數據多個指令去處理呀。因此MISD只是作為理論模型出現,僅僅只在1971年CMU的實驗中出現過,也就是說,實際上并不存在SISD。
1.9.2 存儲器結構分類法
共享存儲器多處理機
共享存儲器多處理機可以理解為一個多核的計算機或者很多個單核的共用一份內存的計算機。當處理器想要處理數據,它就得跑去存儲器拿數據。怎么知道數據在哪呢?通過存儲器上的地址可以知道。
在操作系統中我們學過,如果這個時候兩個處理器要同時在一個存儲器上拿東西,那它們一定要提前溝通好,也就是說,兩個處理器對共享空間的訪問是互斥的。它們提前溝通的工具是互聯網絡。
多處理機系統由多臺獨立的處理機組成,每臺處理機都能夠獨立執行自己的程序和指令流,相互之間通過專門的網絡連接,實現數據的交換和通信,共同完成某項大的計算或處理任務。系統中的各臺處理機由統一的操作系統進行管理,實現指令級以上并行,這種并行性一般是建立在程序段的基礎上,也就是說,多處理機的并行是作業或任務級的并行。共享存儲多處理機系統是指有一個可以被所有處理機訪問的存儲器系統。存儲器系統由一個或多個存儲器模塊組成,所有的存儲器模塊使用一個統一的編址的地址空間。處理機可以用不同的地址訪問不同的存儲器模塊。按存儲器組織方式分類,共享存儲多處理機系統分為集中式共享存儲器系統和分布式共享存儲器系統。
對共享存儲器多處理機進行編程設計到在共享存儲器中存有可由每個處理器執行的代碼。每個程序所需的數據也將存于共享存儲器中。(即程序段和數據段都在共享內存中)。因此如果有需要的話,每個程序可以訪問所有的數據。
程序員要想使用并行計算機的每個處理器來處理一件問題,那原有的高級程序語言就無法使用了。所以為了解決此問題,程序員們開發了一種新的、高級并行程序設計語言,它具有特殊的并行程序設計構造和語句,以聲明共享變量和并行代碼段。雖然想法很好,但是這類并行程序設計語言并不是使用很廣泛。
比較廣泛的做法是在普通的高級程序語言的基礎上生成并行代碼,你可以理解為嵌入式編碼(類似于嵌入式SQL)。此時使用制定好規則的編程語言,然后用預處理器命令對程序的并行部分加以說明即可;這類實踐比較著名的模型就是OpenMP。它是由編譯器命令和構造的一個工業標準,可融入到C/C++中。
另外,我們也可以多開幾條線程,這樣的話給人的感覺也像是并行計算的樣子,不同線程中含有為各個處理器執行的規整的高級語言代碼序列,這些代碼序列可以用來訪問共享單元。但是需要注意的是,實際上用線程的方法不是并行而是并發。
共享存儲器多處理機是很一種很不錯的并行計算機,綜上所述,其方便了對數據的共享。
消息傳遞多計算機
多處理機系統的形式可以通過互聯網絡連接多臺完整的計算機來構成。這實際上是使用了操作系統中的消息傳遞。
在消息傳遞多計算機中,一臺計算機的處理器只能訪問它對應本地的主存儲器,而無法訪問其他計算機上的主存儲器。不同的計算機之間是用互聯網來建立聯系的,通常來說,多個電腦之間通過互聯網傳遞的消息含有的可能是程序所指明的其他計算機處理器進行計算時所需的數據。這種多處理器系統我們通常稱為消息傳遞多處理機(message-passing multiprocessor),或簡稱多計算機。你可以理解為多計算機實質上是真正意義上的分布式存儲計算機。
我們在操作系統常提到進程這個概念,在多計算機上,我們可以把一個問題分成多個并發進程,它們可在各臺計算機上分別執行。如果有6個進程和6個計算機,則我們可在每臺計算機上執行一個進程;如果進程數大于計算機數,那么其中一臺計算機中如果是多核可以采用并行執行,如果是單核可以采用分時方式執行。進程間將通過發送消息的原語來聯系對方。同樣地,發送消息可以采用兩種方式,一種是直接通信方式,一種是間接通信方式,如果感興趣可以去操作系統方面查找資料,這里不再細講。
消息傳遞多計算機比共享存儲器多處理機更容易在物理上進行擴展,也就是說它可以構成較大規模。一般規模比較小的叫做機群(Cluster),規模比較大的叫做超級計算機(SuperComputer),規模很大的叫做數據中心(DataCenter)。
1.10 并行層次和代碼粒度
并行度:同時執行的分進程數
并行粒度:兩次并行或交互操作之間所執行的計算負載
并行度與并行粒度大小?;榈箶?#xff1a;增大粒度會減小并行度
增加并行度會增加系統(同步)開銷
按發送者數量和接受者數量參與通信可將發送分為:
| 延伸:粒度 在并行計算執行過程中,兩個通信之間每個處理器計算工作量大小的粗略描述,分為細粒度和粗粒度。 粒度在并行算法設計中必不可少,通常在進程數與效率之間選擇粒度的大小,比如后面將要介紹的MPI并行程序更適合粗粒度并行,而使用CUDA并行程序就需要細粒度。 |
1.11 并行程序設計模型
2并行計算模型
2.1 拓展:進程
在只有一個用戶的PC機開機的時候,實際上會秘密啟動很多進程。例如,啟動一個進程用來等待進入的電子郵件;或者啟動另一個防病毒進程周期性地檢查是否有病毒庫更新。或者更好笑的是,一開機就是垃圾捆綁軟件,什么2345,什么網頁游戲,這些都是進程。這么多進程的活動都是需要管理的,于是有一個支持多進程的多道程序系統在這里顯得就很有用了。
在任何多道程序設計系統中,CPU能夠很快地切換進程,這個很快是幾百毫秒哦。這也就讓人產生一種并行的錯覺,在一秒鐘內怎么開了這么多進程?同時開的嗎?不是,實際上在一瞬間只能有一個進程讓CPU服務,只是進程切換地太快了,這就是偽并行。這和真正意義上的并行是有區別的,這也導致了此情形可以用來作為判別是否為多處理器系統的指標。
2.2 拓展:進程模型
在操作系統中,進程模型簡稱進程,但實際上和進程有所區別。在進程模型中,計算機上所有可運行的軟件,通常也包括操作系統,被組織成若干順序進程,簡稱進程,進程是程序的一次執行過程。
每個進程都擁有自己的虛擬CPU,當然,實際上真正的CPU在各進程之間來回切換。在操作系統中時間復用技術曾經提到過,當一個資源在時間上復用時,不同的程序或用戶輪流使用它。實際上對于CPU來說也是如此,在時間上進行復用的時候,不同的進程輪流使用它。這種快速地切換是需要特定的設計的,我們稱為多道程序設計。
當然在上述的思考中,我們僅僅討論的是單核CPU,而不是多核。如果是多核CPU,根據我們之前所說,多核CPU可以看成一個大CPU里面裝了多個小的CPU;甚至于有的電腦還不止一個CPU,對于一些并行計算機,多處理器的情況也是很常見的。
拓展:時分復用技術和空分復用技術
這是操作系統系統四大特征——并發、共享、虛擬、異步中虛擬特征的兩大技術。
2.3 拓展:父子進程
在Unix中,通過fork函數創建的新進程是原進程的子進程,而調用fork函數的進程是fork函數創建出來的新進程的父進程。也就是說,通過fork函數創建的新進程與原進程是父子關系,fork就相當于一個憑證,有fork,就有父子關系。
在Windows則沒有這些說法,所有的進程地位都是相同的。
2.4 拓展:線程
在很久以前還沒有引入進程之前,系統中的各個程序只能串行執行。比如你想要邊聽歌邊開QQ,這是不可能做到的,只能先做一件事再做一件事。
后來引入進程后,系統中的各個程序可以并發執行。也就是說,可以同時聽歌和開QQ。但是,即使引入了進程,也不能在QQ中同時視頻聊天和傳輸文件。這是因為操作系統每一次執行都是按照進程為單位來執行的。
從上面的例子來看,進程是程序的一次執行。但是這些功能顯然不可能是由一個程序順序處理就能實現的。有的進程可能需要“同時做很多事”,而傳統的進程只能串行地執行一系列程序。為此,引入了線程來提高并發度。
在傳統中,進程是程序執行流的最小單位,也就是說,CPU每次執行任務,最少執行一個進程。而后在現在,CPU每次執行任務,最少執行一個線程,線程是進程的子集。也就是說,引入線程后,線程成為了程序執行流的最小單位。
需要知道的是,同個進程中所有線程的內存是共享的,如果是同個進程中的線程做通信交換數據非常快,但是不同進程的線程交換數據就很慢了。
2.5 拓展:用戶線程和內核線程
用戶級線程由應用程序通過線程庫實現。所有的線程管理工作都由應用程序負責(包括線程切換)。用戶級線程中,線程切換可以在用戶態下即可完成,無需操作系統干預。在用戶看來,是有多個線程;但是對于操作系統內核來說,并意識不到線程的存在。即用戶級線程對用戶不透明,對操作系統透明。
內核級線程(Kernel-Level Thread,KTL,又稱為“內核支持的線程”)。內核級線程的管理工作由操作系統內核完成。線程調度、切換等工作都由內核負責,因此內核級線程的切換必然需要在核心態下才能完成。
2.6 POSIX線程
為了實現可移植的線程程序,IEEE定義了線程的標準。它定義的線程包叫做pthread,大部分UNIX系統支持該標準。這個標準定義了超過60個函數調用。常見的幾個如下所示:
?一言蔽之:Posix線程是一種標準,我們可以在任何編程語言中使用這個標準,如Java如果要開多線程就實現Thread這個類,這個類中的所有方法都是按照Posix這個標準制定的。
Posix線程模型具有如下特點:
2.7 并行算法
串行算法:解題方法的精確描述,是一組有窮的規則,它們規定了解決某一特定類型問題的一系列運算。
并行算法:一些可同時執行的諸進程的集合,這些進程互相作用和協調動作從而達到給定問題的求解。
描述語言:采用偽代碼進行描述,在程序描述語言中引入并行語句
同步:在時間上強使各執行進程在某一點必須互相等待
通信:共享存儲多處理器使用讀寫全局變量,分布存儲多計算機使用發送和接收消息。
| 拓展:進程通信 在操作系統中,進程通信就是進程之間的信息交換。 進程是分配系統資源的單位(包括內存地址空間),因此各進程擁有的內存地址空間相互獨立。為了保證安全,一個進程不能直接訪問另一個進程的地址空間,但是進程之間的信息交換又是必須實現的,為了保證進程之間的安全通信,操作系統提供了一些方法。 共享存儲 使用共享存儲的方式進行進程通信的話,操作系統會在內存中開辟一個共享空間,讓兩個進程進行通信。 需要注意的是:兩個進程對共享空間的訪問必須是互斥的(互斥訪問通過操作系統提供的工具實現);并且操作系統只負責提供共享空間和同步互斥工具。 管道通信 管道是指用于連接讀寫進程的一個共享文件,又名pipe文件。其實就是在內存中開辟一個大小固定的緩沖區。需要知道的是: 1. 管道只能采用半雙工通信,某一個時間段內只能實現單向的傳輸。如果要實現雙向同時通信,則需要設置兩個管道。 2. 各進程要互斥地訪問管道 3. 數據以字符流的形式寫入管道,當管道寫滿時,寫進程的write()系統調用將被阻塞,等待讀進程將數據取走。當讀進程將數據全部取走后,管道變空,此時讀進程的read()系統調用將被阻塞。 4. 如果沒寫滿,就不允許讀;如果沒讀空,就不允許寫。 5. 數據一旦被讀出,就從管道中被拋棄,這就意味著讀進程最多只能有一個,否則可能會有讀錯數據的情況。 消息傳遞 進程間的數據交換以格式化的消息為單位。進程通過操作系統提供的“發送消息/接受消息”兩個原語進行數據交換。 一個格式化的消息可以分為消息頭和消息體。消息頭包括:發送進程ID、接受進程ID、消息類型、消息長度等格式化的信息(計算機網絡中發送的“報文”其實就是一種格式化的消息)。 消息傳遞也分為兩種方式:
|
2.8 并行計算模型
2.9 并行算法一般設計過程
PCAM設計方法學
劃分:分解成小任務,開拓并發性
通訊:確定諸任務間的數據交換,監測劃分的合理性
組合:依據任務的局部性,組合成更大的任務
映射:將每個任務分配到處理器上,提高算法的性能(負載均衡)
一二階段:考慮并發性、可擴放性,尋求具有這些特征的并行算法,即前期主要考慮如并發性等與機器無關的特性。
三四階段: 將注意力放在局部性及其它與性能有關的特性上,即后期考慮與機器有關的特性。
2.10 程序性能評價與優化
并行執行時間=計算時間+并行開銷時間+相互通信時間
存儲器性能:估計存儲器的帶寬B
并行與通信開銷的測量:乒乓方法
加速比性能定律
我們用p表示處理器數,用Wp表示使用具有p個處理器的多處理機的執行所需的時間,Ws表示使用單處理器系統執行時間。
Amdahl定律:固定負載的加速公式S=Ws+wp/ws+wp/p,為了歸一化可將Ws+Wp
看做f+1-f。對加速公式求極限,當p趨近與無窮時,極限為S = 1/f。這表明了隨著處理器數目的無限增多,并行系統所能達到的加速之上限為1/f。
Gustafson定律:S=Ws+pWp/Ws+Wp。這表明隨著處理器數目的增加,加速幾乎與處理器數成比例的線性增加,串行比例f不再是程序的瓶頸。
3 OpenMP并行編程模型
3.1 OpenMP概述
OpenMP是由OpenMP Architecture Review Board牽頭提出的,并已被廣泛接受。其所支持的語言包括C、C++、Fortran。
OpenMP采用fork-join的執行模式,開始的時候只存在一個主線程,當需要進行并行計算的時候,派生出若干個分支線程來執行并行任務。當并行代碼執行完成之后,分支線程會合,并把控制流程交給單獨的主線程。
3.2 OpenMP語句模式
OpenMP通過編譯指導命令來并行化,什么是編譯指導命令?簡單來說就是我們平常寫的#開頭的語句,通過程序中插入的這些編譯指導命令,計算機就會完成并行計算的工作。在C/C++程序中,OpenMP的所有的編譯指導命令都是以#pragma omp開始的,后面跟具體的功能指導命令,命令形式如下:
#pragma omp 指令 子句,子句,子句……
注意:由于我不太會C,所以這里使用C++。如果是第一次使用C++的話,可以簡單理解為C++和C在以下代碼中的不同僅限于輸出是使用cout,而C使用printf。C++換行采用endl。且將要輸出的東西由<<流向cout。
3.3 OpenMP簡單演示
我們先從最簡單的一個并行程序開始。在下面的代碼中,我們只用parallel制導命令開啟并行域,需要注意的是,如果不指定線程數的話默認啟用與CPU核心數同等的線程數。
| #include<omp.h> #include<iostream> using namespace std; int main() { #pragma omp parallel ??? { ???????? cout << "Hello, world!" << endl; ??? } } |
可以看出,從#pragma omp parallel開始的花括號內就是并行域。parallel制導命令表示接下來由花括號括起來的區域將創建多個線程并行執行。
我們還可以使用num_threads子句來控制線程的個數,需要注意的是,一般設置的線程數不超過CPU核心數,如下:
| #include<omp.h> #include<iostream> using namespace std; int main() { ??? omp_set_num_threads(2);//指定線程數為2 ??? #pragma omp parallel ??? { ???????? cout << "Hello, world!" << endl; ??? } } |
我們可以使用制導命令for來提升for循環迭代的速度。并且可以使用omp_get_thread_num()查看對應任務在并行域中使用的線程號。在下面的代碼演示中,我使用了for循環來循環4次,每次循環中打印本次循環使用的線程號。我指定了兩條線程,線程號從0開始,說明任務只會使用0號線程或者1號線程。
| #include<omp.h> #include<iostream> using namespace std; int main() { ??? omp_set_num_threads(2); #pragma omp parallel ??? { #pragma omp for ???????? for (int i = 0; i < 4; i++) ???????????? cout << omp_get_thread_num() << endl; ??? } } |
OpenMP實際上允許for寫在parallel后面,即#pragma omp parallel for,不過這樣寫的壞處是會踩坑,所以平時建議不要這么寫。
3.4 Schedule關于for循環的調度
在以上的演示中,我們發現任務是隨機分配到各個線程上的,我們并沒有做任何的調度。在下面的介紹中,我們使用schedule制導來進行for循環的調度。
schedule的基本形式是schedule(type, size),其中type參數有四種,分別是:1.static, 2.dynamic, 3.guided, 4.runtime,而size參數時整型數據,其表示循環迭代次數劃分的單位。
static參數
static表示靜態調度,這時候不用size參數,分配給每個程序的都是n/t次迭代,n為迭代次數,t為并行的線程數目。在下面的代碼中,我指定了兩條線程,且循環8次,則實際迭代次數只有4次。
| #include<omp.h> #include<iostream> using namespace std; int main() { ??? omp_set_num_threads(2); #pragma omp parallel for schedule(static) ??? for (int i = 0; i < 8; i++) ???????? cout << omp_get_thread_num() << endl; } |
dynamic參數
動態調度模式是先到先得的方式進行任務分配,不用size參數的時候,先把任務干完的線程先取下一個任務,以此類推,而不是一開始就分配固定的任務數。使用size參數的時候,分配的任務以size為單位,一次性分配size個。雖然很智能,在任務難度不均衡的時候適合用dynamic,否則會引起過多的任務動態申請的開銷。
guided參數
剛開始每個線程會分配到比較大的迭代塊,后來分配到的迭代塊逐漸遞減,沒有指定size就會降到1,否則降到size。
runtime參數
基本不會用到
3.5 設置環境變量(拓展)
這里設置環境變量你可以理解為在外面設置好的規則,程序內都必須遵從這個規則。常見的環境變量有:
OMP_SCHEDULE:用于for和parallel for中,決定了循環的各個迭代如何在處理中進行分配。
OMP_NUM_THREADS:定義執行中所能使用的最大線程數。
OMP_DYNAMIC:確定是否動態設定并行域執行部分的線程數
OMP_NESTED:確定是否允許嵌套并行
3.6 sections制導指令
用sections把不同的區域交給不同的線程去執行。在下面的代碼中,我開啟三條線程,并且使用section制導開啟三塊區域,每個區域由一個線程所負責。
| #include<omp.h> #include<iostream> using namespace std; int main() { ??? omp_set_num_threads(3); #pragma omp parallel sections ??? { #pragma omp section ???????? { ???????????? cout << omp_get_thread_num(); ???????? } #pragma omp section ???????? { ???????????? cout << omp_get_thread_num(); ???????? } #pragma omp section ???????? { ???????????? cout << omp_get_thread_num(); ???????? } ??? } } |
3.7 single制導指令(拓展)
single制導指令所包含的代碼段只有一個線程執行,別的線程跳過該代碼,如果沒有nowait子句,那么其他線程將會在single制導指令結束的隱式同步點等待,有nowait子句則其他線程將跳過等待往下執行。在下面的代碼中,我開啟四條線程,可以發現,只有一條線程服務于single制導命令下的代碼段。
| #include<omp.h> #include<iostream> using namespace std; int main() { ??? omp_set_num_threads(4); #pragma omp parallel ??? { #pragma omp single ???????? { ???????????? cout << "single thread=" << omp_get_thread_num() << endl; ???????? } ???????? cout << omp_get_thread_num() << endl; ??? } } |
3.8 共享任務結構
3.9 OpenMP的優點和缺點
優點
缺點
3.10 常用子句的補充
private:指定每個線程都有它自己的變量私有副本
firstprivate:指定每個線程都有它自己的變量私有副本,并且變量要被繼承主線程中的初值。
lastprivate:主要是用來指定將線程中的私有變量的值在并行處理結束后復制回主線程中的對應變量。
nowait:忽略指定中暗含的等待
num_threads:指定線程的個數
schedule:指定如何調度for循環迭代
shared:指定一個或多個變量為多個線程間的共享變量
ordered:用來指定for循環的執行要按順序執行
copyprivate:用于single指令中的指定變量為多個線程的共享變量
copyin:用來指定一個threadprivate的變量的值要用主線程的值進行初始化。
default:用來指定并行處理區域內的變量的使用方式,缺省是shared。
4 MPI并行編程模型
4.1 拓展:什么是MPI
MPI是一個跨語言的通訊協議,用于編寫并行計算機。支持點對點和廣播。MPI的目標是高性能,大規模性,和可移植性。MPI在今天仍為高性能計算的主要模型。
主要的MPI-1模型不包括共享內存概念,MPI-2只有有限的分布共享內存概念。 但是MPI程序經常在共享內存的機器上運行。在MPI模型周邊設計程序比在NUMA架構下設計要好因為MPI鼓勵內存本地化。
MPI是一個在平行計算中傳遞消息的庫的標準,由實現人員和使用人員來遵守。目前的實現版本有MPICH2, Argonne National Laboratory實現,他還有好幾個派生子項目。
4.2 MPI基本函數
int MPI_Init(int* argc,char** argv[])
int MPI_Finalize (void)
int MPI_Comm_size (MPI_Comm comm ,int* size )
int MPI_Comm_rank (MPI_Comm comm ,int* rank)
得到本進程在通信空間中的rank值,即在組中的邏輯編號(該 rank值為0到p-1間的整數,相當于進程的ID。)
int MPI_Send( void *buff, int count, MPI_Datatype datatype, int dest, int tag, MPI_Comm comm)
int MPI_Recv( void *buff, int count, MPI_Datatype datatype, int source, int tag, MPI_Comm comm, MPI_Status *status)
4.3 消息傳遞的特點
在消息傳遞模型中,一個并行應用由一組進程組成,每個進程的代碼是本地的,只能訪問私有數據,進程之間通過傳遞消息實現數據共享和進程同步。
優點:用戶可以對并行性的開發、數據分布和通信實現完全控制。
缺點:
總結
- 上一篇: 主动轮廓模型——Snake分割算法(MA
- 下一篇: 小秘书的福音——使用Word VBA打造