（Open MultiProcessing）是一套支援跨平臺共享記憶體方式的多執行緒併發的程式設計 API，使用 CC 和 Fortran 語言，可以在大多數的處理器體系和作業系統中執行，包括 Solaris AIX HPUX GNULinux Mac OS X 和 Microsoft Windows。包括一套編譯器指令、庫和一些能夠影響執行行為的環境變數。 OpenMP 採用可移植的、可擴充套件的模型，為程式設計師提供了一個簡單而靈活的開發平臺，從標準桌面電腦到超級計算機的並行應用程式介面。 混合並行程式設計模型構建的應用程式可以同時使用 OpenMP 和 MPI，或更透明地透過使用 OpenMP 擴充套件的非共享記憶體系統上執行的計算機叢集。 OpenMP 是由 OpenMP Architecture Review Board 牽頭提出的，並已被廣泛接受的，用於共享記憶體並行系統的多執行緒程式設計的一套指導性註釋（Compiler Directive）。OpenMP 支援的程式語言包括 C 語言、C 和 Fortran；而支援 OpenMP 的編譯器包括 Sun Studio 和 Intel Compiler，以及開放原始碼的 GCC、LLVM 和 Open64 編譯器。OpenMP 提供了對並行演算法的高層的抽象描述，程式設計師透過在原始碼中加入專用的 pragma 來指明自己的意圖，由此編譯器可以自動將程式進行並行化，並在必要之處加入同步互斥以及通訊。當選擇忽略這些 pragma，或者編譯器不支援 OpenMP 時，程式又可退化為通常的程式（一般為序列），程式碼仍然可以正常運作，只是不能利用多執行緒來加速程式執行。 介紹 OpenMP 是一個跨平臺的多執行緒實現，主執行緒 順序的執行指令 生成一系列的子執行緒，並將任務劃分給這些子執行緒進行執行。這些子執行緒並行的執行，由執行時環境將執行緒分配給不同的處理器。 要進行並行執行的程式碼片段需要進行相應的標記，用預編譯指令使得在程式碼片段被執行前生成執行緒，每個執行緒會分配一個 id，可以透過函式 called omp_get_thread_num 來獲得該值，該值是一個整數，主執行緒的 id 為 0。在並行化的程式碼執行結束後，子執行緒 join 到主執行緒中，並繼續執行程式。 預設情況下，各個執行緒獨立地執行並行區域的程式碼。可以使用 Worksharing constructs 來劃分任務，使每個執行緒執行其分配部分的程式碼。透過這種方式，使用 OpenMP 可以實現任務並行和資料並行。 執行時環境分配給每個處理器的執行緒數取決於使用方法、機器負載和其他因素。執行緒的數目可以透過環境變數或者程式碼中的函式來指定。在 CC 中，OpenMP 的函式都宣告在標頭檔案 omph 中。 歷史 OpenMP Architecture Review Board ARB 於 1997 年 10 月釋出了 OpenMP for Fortran 10。次年的 10 月，釋出了 CC 的標準。2000 年，釋出了 Fortran 語言的 20 版本，並於 2002 年釋出了 CC 語言的 20 版本。2005 年，包含 Fortran 和 CC 的 25 版本釋出了。 在 2008 年 5 月釋出了 30 版。30 中的新功能包括任務 tasks 和任務結構 task construct 的概念。這些新功能總結在 OpenMP30 規範的附錄 F 中。 OpenMP 規範的 31 版於 2011 年 7 月 9 日釋出。 40 版本在 2013 年 7 月釋出，它增加或改進以下功能：支援加速器，原子性，錯誤處理，執行緒關聯，任務擴充套件，減少使用者定義的 SIMD 支援和 Fortran 2003 的支援。 特色 OpenMP 提供的這種對於並行描述的高層抽象降低了並行程式設計的難度和複雜度，這樣程式設計師可以把更多的精力投入到並行演算法本身，而非其具體實現細節。對基於資料分集的多執行緒程式設計，OpenMP 是一個很好的選擇。同時，使用 OpenMP 也提供了更強的靈活性，可以較容易的適應不同的並行系統配置。執行緒粒度和負載平衡等是傳統多執行緒程式設計中的難題，但在 OpenMP 中，OpenMP 庫從程式設計師手中接管了部分這兩方面的工作。 語法 pragma omp directive clause clause directive 其中，directive 共 11 個： atomic 記憶體位置將會原子更新（Specifies that a memory location that will be updated atomically） barrier 執行緒在此等待，直到所有的執行緒都執行到此 barrier。用來同步所有執行緒。 critical 其後的程式碼塊為臨界區，任意時刻只能被一個執行緒執行。 flush 所有執行緒對所有共享物件具有相同的記憶體檢視（view of memory） for 用在 for 迴圈之前，把 for 迴圈並行化由多個執行緒執行。迴圈變數只能是整型 master 指定由主執行緒來執行接下來的程式。 ordered 指定在接下來的程式碼塊中，被並行化的 for 迴圈將依序執行（sequential loop） parallel 代表接下來的程式碼塊將被多個執行緒並行各執行一遍。 sections 將接下來的程式碼塊包含將被並行執行的 section 塊。 single 之後的程式將只會在一個執行緒（未必是主執行緒）中被執行，不會被並行執行。 threadprivate 指定一個變數是執行緒區域性儲存（thread local storage） clause 共計 13 個 clause： copyin 讓 threadprivate 的變數的值和主執行緒的值相同。 copyprivate 不同執行緒中的變數在所有執行緒中共享。 default Specifies the behavior of unscoped variables in a parallel region firstprivate 對於執行緒區域性儲存的變數，其初值是進入並行區之前的值。 if 判斷條件，可用來決定是否要並行化。 lastprivate 在一個迴圈並行執行結束後，指定變數的值為迴圈體在順序最後一次執行時取得的值，或者 pragma sections 在中，按文字順序最後一個 section 中執行取得的值。 nowait 忽略 barrier 的同步等待。 num_threads 設定執行緒數量的數量。預設值為當前計算機硬體支援的最大併發數。一般就是 CPU 的核心數目。超執行緒被作業系統視為獨立的 CPU 核心。 ordered 使用於 for，可以在將迴圈並行化的時候，將程式中有標記 directive ordered 的部份依序執行。 private 指定變數為執行緒區域性儲存。 reduction Specifies that one or more variables that are private to each thread are the subject of a reduction operation at the end of the parallel region schedule 設定 for 迴圈的並行化方法；有 dynamic、guided、runtime、static 四種方法。 schedule static chunk_size 把 chunk_size 數目的迴圈體的執行，靜態依序指定給各執行緒。 schedule dynamic chunk_size 把迴圈體的執行按照 chunk_size（預設值為 1）分為若干組（即 chunk），每個等待的執行緒獲得當前一組去執行，執行完後重新等待分配新的組。 schedule guided chunk_size 把迴圈體的執行分組，分配給等待執行的執行緒。最初的組中的迴圈體執行數目較大，然後逐漸按指數方式下降到 chunk_size。 schedule runtime 迴圈的並行化方式不在編譯時靜態確定，而是推遲到程式執行時動態地根據環境變數 OMP_SCHEDULE 來決定要使用的方法。 shared 指定變數為所有執行緒共享。 OpenMP 的庫函式 OpenMP 定義了 20 多個庫函式： 1void omp_set_num_threads int _Num_threads 在後續並行區域設定執行緒數，此呼叫隻影響呼叫執行緒所遇到的同一級或內部巢狀級別的後續並行區域。說明：此函式只能在序列程式碼部分呼叫 2int omp_get_num_threads void 返回當前執行緒數目。說明：如果在序列程式碼中呼叫此函式，返回值為 1 3int omp_get_max_threads void 如果在程式中此處遇到未使用 num_threads 子句指定的活動並行區域，則返回程式的最大可用執行緒數量。說明：可以在序列或並行區域呼叫，通常這個最大數量由 omp_set_num_threads 或 OMP_NUM_THREADS 環境變數決定 4int omp_get_thread_num void 返回當前執行緒 idid 從 1 開始順序編號，主執行緒 id 是 0 5int omp_get_num_procs void 返回程式可用的處理器數 6void omp_set_dynamic int _Dynamic_threads 啟用或停用可用執行緒數的動態調整預設情況下啟用動態調整 此呼叫隻影響呼叫執行緒所遇到的同一級或內部巢狀級別的後續並行區域。如果 _Dynamic_threads 的值為非零值，啟用動態調整；否則，停用動態調整 7int omp_get_dynamic void 確定在程式中此處是否啟用了動態執行緒調整。啟用了動態執行緒調整時返回非零值；否則，返回零值 8int omp_in_parallel void 確定執行緒是否在並行區域的動態範圍內執行。如果在活動並行區域的動態範圍內呼叫，則返回非零值；否則，返回零值。活動並行區域是指 IF 子句求值為 TRUE 的並行區域 9void omp_set_nested int _Nested 啟用或停用巢狀並行操作。此呼叫隻影響呼叫執行緒所遇到的同一級或內部巢狀級別的後續並行區域_Nested 的值為非零值時啟用巢狀並行操作；否則，停用巢狀並行操作。預設情況下，停用巢狀並行操作 10int omp_get_nested void 確定在程式中此處是否啟用了巢狀並行操作。啟用巢狀並行操作時返回非零值；否則，返回零值 互斥鎖操作 巢狀鎖操作 功能 11void omp_init_lock omp_lock_t _Lock 12 void omp_init_nest_lock omp_nest_lock_t _Lock 初始化一個（巢狀）互斥鎖 13void omp_destroy_lock omp_lock_t _Lock 14void omp_destroy_nest_lock omp_nest_lock_t _Lock 結束一個（巢狀）互斥鎖的使用並釋放記憶體 15void omp_set_lock omp_lock_t _Lock 16void omp_set_nest_lock omp_nest_lock_t _Lock 獲得一個（巢狀）互斥鎖 17void omp_unset_lock omp_lock_t _Lock 18void omp_unset_nest_lock omp_nest_lock_t _Lock 釋放一個（巢狀）互斥鎖 19int omp_test_lock omp_lock_t _Lock 20int omp_test_nest_lock omp_nest_lock_t _Lock 試圖獲得一個（巢狀）互斥鎖，並在成功時放回真（true） 失敗是返回假（false） 21double omp_get_wtime void 獲取 wall clock time 返回一個 double 的數，表示從過去的某一時刻經歷的時間，一般用於成對出現，進行時間比較 此函式得到的時間是相對於執行緒的，也就是每一個執行緒都有自己的時間 22double omp_get_wtick void 得到 clock ticks 的秒數 例子 在 omp parallel 段內的程式程式碼由多執行緒來執行： int main int argc char argv pragma omp parallel printf Hello worldn return 1 執行結果 gcc ompc （由單執行緒來執行） aout Hello world gcc fopenmp ompc （由多執行緒來執行） aout Hello world Hello world Hello world Hello world 環境變數 OpenMP 可以使用環境變數 OMP_NUM_THREADS 以控制執行執行緒的數量。 例子 gcc fopenmp ompc setenv OMP_NUM_THREADS 2（由 2 執行緒來執行） setenv 是 CSH 的指令 在 bash shell 環境中 要用 export export OMP_NUM_THREADS2 （由 2 執行緒來執行） aout Hello world Hello world 優點和缺點 優點 可移植的多執行緒程式碼（在 CC 和其他語言中，人們通常為了獲得多執行緒而呼叫特定於平臺的原語） 簡單，沒必要象 MPI 中那樣處理訊息傳遞 資料分佈和分解由指令自動完成 增量並行，一次可以只在程式碼的一部分執行，對程式碼不需要顯著的改變 統一的順序執行和並行執行的程式碼，在順序執行編譯器上，OpenMP 的執行按照註釋進行對待； 在一般情況下，使用 OpenMP 並行時原始的（序列）程式碼語句不需要進行修改，這減少不經意間引入錯誤的機會。 同時支援粗粒度和細粒度的並行 可以在 GPU 上使用 缺點 存在引入難以除錯的同步錯誤和競爭條件的風險 目前，只能在共享記憶體的多處理器平臺高效執行 需要一個支援 OpenMP 的編譯器 可擴充套件性是受到記憶體架構的限制 不支援比較和交換 缺乏可靠的錯誤處理 缺乏對執行緒與處理器對映的細粒度控制 很容易出現一些不能共享的程式碼 多執行緒的可執行檔案的啟動需要更多的時間，可能比單執行緒的執行的慢，因此，使用多執行緒一定要有其他有優勢的地方 很多情況下使用多執行緒不僅沒有好處，還會帶來一些額外消耗 爭議 作為高層抽象，OpenMP 並不適合需要複雜的執行緒間同步和互斥的場合。 OpenMP 的另一個缺點是不能在非共享記憶體系統（如計算機叢集）上使用。在這樣的系統上，MPI 使用較多。 編譯器支援 主流 CC 編譯器，如 gcc 與 visual C，都內在支援 OpenMP。一般都必須在程式中 include omph gcc 編譯時需使用編譯選項 fopenmp。但是，如果編譯為目標檔案與連結生成可執行檔案是分開為兩步操作，那麼連結時需要給出附加庫 gomp，否則會在連結時報錯 undefined reference to omp_get_thread_num。 Visual C 需要在 IDE 的編譯選項 語言 支援 OpenMP。這實際上使用了編譯選項 openmp 參見 分叉會合模型 參考文獻 外部連結 OpenMP 官方網站 CategoryC 語言家族 Category 併發計算 Category 應用程式介面