1. 程式人生 > >OpenMP入門教程(二)

OpenMP入門教程(二)

OpenMP API概述

OpenMP由三部分組成:

  • 編譯指令(19)
  • 執行時庫程式(32)
  • 環境變數(9)

後來的API包含同樣的三個元件,只是三者的數量都有所增加。

 

編譯器指令

OpenMP編譯器指令用於各種目的:

  • 產生平行區域
  • 線上程之間劃分程式碼塊
  • 線上程之間分配迴圈迭代
  • 序列化程式碼段
  • 執行緒之間的工作同步

格式如下

#pragma omp <directive> [clause[[,] clause] ...]

通用規則:

  • 區分大小寫
  • 指令遵循編譯指令的C/C++規則
  • 每個指令只能指定一個指令名
  • 每個指令最多使用一個後續語句,該語句必須是結構化塊
  • 通過在指令行末尾用反斜槓(“\”)轉義換行符,可以在後續行上“繼續”長指令行

並行區域構造:

並行區域是將由多個執行緒執行的程式碼塊。這是基本的OpenMP並行構造。

 1 #pragma omp parallel [clause ...] newline 
 2                      if (scalar_expression) 
 3                      private (list) 
 4                      shared (list) 
5 default(shared | none) 6 firstprivate (list) 7 reduction (operator:list) 8 copyin (list) 9 num_threads (integer-expression) 10 11 12 structured_block

暫時先解釋以下num_threads語句(其它見另外一篇部落格,有點多

這裡的執行緒數是多少呢?決定執行緒數的因素有多個,它們的優先順序如下:

  1. if語句的值
  2. 設定num_threads語句
  3. 使用的omp_set_num_threads() 庫函式
  4. 設定的OMP_NUM_THREADS 環境變數

注意:生成的執行緒編號為0~N,其中0是主執行緒(master thread)的編號

指令(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個

  • 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 四種方法。shared 指定變數為所有執行緒共享。
    • 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 指定變數為所有執行緒共享。

OpenmMP的庫函式(Run-Time Library  Routines)

共22個:

1.void omp_set_num_threads(int _Num_threads);

在後續並行區域設定執行緒數,此呼叫隻影響呼叫執行緒所遇到的同一級或內部巢狀級別的後續並行區域.說明:此函式只能在序列程式碼部分呼叫.

2.int omp_get_num_threads(void);

返回當前執行緒數目.說明:如果在序列程式碼中呼叫此函式,返回值為1.

3.int omp_get_max_threads(void);

如果在程式中此處遇到未使用 num_threads() 子句指定的活動並行區域,則返回程式的最大可用執行緒數量.說明:可以在序列或並行區域呼叫,通常這個最大數量由omp_set_num_threads()或OMP_NUM_THREADS環境變數決定.

4.int omp_get_thread_num(void);

返回當前執行緒id.id從1開始順序編號,主執行緒id是0.

5.int omp_get_num_procs(void);

返回程式可用的處理器數.

6.void omp_set_dynamic(int _Dynamic_threads);

啟用或禁用可用執行緒數的動態調整.(預設情況下啟用動態調整.)此呼叫隻影響呼叫執行緒所遇到的同一級或內部巢狀級別的後續並行區域.如果 _Dynamic_threads 的值為非零值,啟用動態調整;否則,禁用動態調整.

7.int omp_get_dynamic(void);

確定在程式中此處是否啟用了動態執行緒調整.啟用了動態執行緒調整時返回非零值;否則,返回零值.

8.int omp_in_parallel(void);

確定執行緒是否在並行區域的動態範圍內執行.如果在活動並行區域的動態範圍內呼叫,則返回非零值;否則,返回零值.活動並行區域是指 IF 子句求值為 TRUE 的並行區域.

9.void omp_set_nested(int _Nested);

啟用或禁用巢狀並行操作.此呼叫隻影響呼叫執行緒所遇到的同一級或內部巢狀級別的後續並行區域._Nested 的值為非零值時啟用巢狀並行操作;否則,禁用巢狀並行操作.預設情況下,禁用巢狀並行操作.

10.int omp_get_nested(void);

確定在程式中此處是否啟用了巢狀並行操作.啟用巢狀並行操作時返回非零值;否則,返回零值.

互斥鎖操作 巢狀鎖操作 功能

11.void omp_init_lock(omp_lock_t * _Lock); 12. void omp_init_nest_lock(omp_nest_lock_t * _Lock);

初始化一個(巢狀)互斥鎖.

13.void omp_destroy_lock(omp_lock_t * _Lock); 14.void omp_destroy_nest_lock(omp_nest_lock_t * _Lock);

結束一個(巢狀)互斥鎖的使用並釋放記憶體.

15.void omp_set_lock(omp_lock_t * _Lock); 16.void omp_set_nest_lock(omp_nest_lock_t * _Lock);

獲得一個(巢狀)互斥鎖.

17.void omp_unset_lock(omp_lock_t * _Lock); 18.void omp_unset_nest_lock(omp_nest_lock_t * _Lock);

釋放一個(巢狀)互斥鎖.

19.int omp_test_lock(omp_lock_t * _Lock); 20.int omp_test_nest_lock(omp_nest_lock_t * _Lock);

試圖獲得一個(巢狀)互斥鎖,並在成功時放回真(true),失敗是返回假(false).

21.double omp_get_wtime(void);

獲取wall clock time,返回一個double的數,表示從過去的某一時刻經歷的時間,一般用於成對出現,進行時間比較. 此函式得到的時間是相對於執行緒的,也就是每一個執行緒都有自己的時間.

22.double omp_get_wtick(void);

得到clock ticks的秒數.

 

對這些指令詳細的解釋及應用,可見這篇部落格

 

參考連結:

1、https://computing.llnl.gov/tutorials/openMP/#Abstract

2、https://www.wikiwand.com/zh-cn/OpenMP