承接前面兩篇,這里直接逐一介紹和使用有關OpenMP的指令和函數
Directives
1、for
作用:for指令指定緊隨其后的程序的循環的迭代必須由團隊並行執行,只是假設已經建立了並行區域,否則它在單個處理器上串行執行。
格式:
1 #pragma omp for [clause ...] newline 2 schedule (type [,chunk]) 3 ordered 4 private (list) 5 firstprivate (list) 6 lastprivate (list) 7 shared (list) 8 reduction (operator:list) 9 collapse (n) 10 nowait for_loop
可以使用如下子句:
還可以通過Schedule子句(clause)設置for循環的並行化方法:(有關一種調度如何比其他調度更優化的討論,請參閱http://openmp.org/forum/viewtopic.php?f=3&t=83)
- static:循環迭代被分成size chunk,然后靜態的分配給各個線程,如果chunk沒有被指定,則均勻地划分(如果可能)給各個線程
- dynamic:循環迭代被分成size chunk,然后動態地分配給各個線程,當一個chunk完成時,被分配另外一個chunk。默認地chunk size為1
- guided:當線程請求循環迭代時,迭代會動態地分配給塊中地線程,直到沒有剩余的塊要被分配。與dynamic類似,不同的地方在於每次為線程分配chunk時都會變小,所以最初組中的循環體執行數目較大。初始大小與以下成正比,number_of_iterations / number_of_thread,后續塊與之成比例,number_of_iterations_remaining / number_of_threads。
- runtime: 循環的並行化方式不在編譯時靜態確定,而是推遲到程序執行時動態地根據環境變量OMP_SCHEDULE 來決定要使用的方法。此時在子句中指定chunk_size是非法的
- auto:調度決策取決於編譯器/運行時系統
nowait子句:如果指定,則線程在循環結束時不同步
ordered子句:指定必須像在串行程序中一樣執行循環的迭代,可以對for的部分使用
collapse子句:指定嵌套循環中應將多少循環折疊到一個大的迭代空間中,並根據schedule子句進行划分 。折疊迭代空間中的迭代順序被確定為順序執行它們。可以改善表現。
其它的子句后面會做介紹
限制:
- 循環迭代變量必須是整數,並且所有線程的循環控制參數必須相同
- 程序正確性不能取決於哪個線程執行特定迭代,需要確保程序的正確性
- 從for指令關聯的循環中分支是非法的
- 必須將塊大小指定為循環不變整數表達式,因為在不同線程的評估期間沒有同步
示例
1 #include <omp.h> 2 #define N 1000 3 #define CHUNKSIZE 100 4 5 main(int argc, char *argv[]) { 6 7 int i, chunk; 8 float a[N], b[N], c[N]; 9 10 /* Some initializations */ 11 for (i=0; i < N; i++) 12 a[i] = b[i] = i * 1.0; 13 chunk = CHUNKSIZE; 14 15 #pragma omp parallel shared(a,b,c,chunk) private(i) 16 { 17 18 #pragma omp for schedule(dynamic,chunk) nowait 19 for (i=0; i < N; i++) 20 c[i] = a[i] + b[i]; 21 22 } /* end of parallel region */ 23 24 }
2、section
作用:section是一種非迭代的工作共享結構,代碼被划分成多個區域
格式:
1 #pragma omp sections [clause ...] newline 2 private (list) 3 firstprivate (list) 4 lastprivate (list) 5 reduction (operator: list) 6 nowait 7 { 8 9 #pragma omp section newline 10 11 structured_block 12 13 #pragma omp section newline 14 15 structured_block 16 17 }
注意:
- 除非使用nowait子句,否則sections指令結尾都有一個隱含的障礙
- 分區塊里不能含有分支
示例
1 #include <omp.h> 2 #define N 1000 3 4 main(int argc, char *argv[]) { 5 6 int i; 7 float a[N], b[N], c[N], d[N]; 8 9 /* Some initializations */ 10 for (i=0; i < N; i++) { 11 a[i] = i * 1.5; 12 b[i] = i + 22.35; 13 } 14 15 #pragma omp parallel shared(a,b,c,d) private(i) 16 { 17 18 #pragma omp sections nowait 19 { 20 21 #pragma omp section 22 for (i=0; i < N; i++) 23 c[i] = a[i] + b[i]; 24 25 #pragma omp section 26 for (i=0; i < N; i++) 27 d[i] = a[i] * b[i]; 28 29 } /* end of sections */ 30 31 } /* end of parallel region */ 32 33 }
3、其它的不一一介紹了,請參閱:OpenMP
Clause
前面已經介紹了幾個子句,這里主要介紹數據作用域子句。
1、private
作用:private子句將其列表中的變量聲明為每個線程的私有變量
格式:
private (list)
要點:
- 在組中的每個線程聲明一個相同數據類型的變量
- 所有對原始變量的引用全部替換為對新變量的引用
- 被聲明為private的變量應被認為未初始化
2、shared
作用:shared子句聲明其列表中的變量,以便在團隊中的所有線程之間共享
格式:
shared (list)
要點:
- 共享變量僅存在於一個內存位置,並且所有線程都可以讀取或寫入該地址
- 程序員有責任確保多個線程正確訪問SHARED變量(例如通過CRITICAL部分)
3、reduction
作用:reduction子句對列表中的每個變量執行簡化操作。為每個線程創建並初始化每個列表變量的私有副本。在縮減結束時,reduce變量應用於共享變量的所有私有副本,最終結果將寫入全局共享變量。
格式:
1 reduction (operator: list)
示例:
並行循環的迭代將以相同大小的塊分配給團隊中的每個線程(SCHEDULE STATIC);
在並行循環結構的末尾,所有線程將添加其“result”值以更新主線程的全局副本;
1 #include <omp.h> 2 3 main(int argc, char *argv[]) { 4 5 int i, n, chunk; 6 float a[100], b[100], result; 7 8 /* Some initializations */ 9 n = 100; 10 chunk = 10; 11 result = 0.0; 12 for (i=0; i < n; i++) { 13 a[i] = i * 1.0; 14 b[i] = i * 2.0; 15 } 16 17 #pragma omp parallel for \ 18 default(shared) private(i) \ 19 schedule(static,chunk) \ 20 reduction(+:result) 21 22 for (i=0; i < n; i++) 23 result = result + (a[i] * b[i]); 24 25 printf("Final result= %f\n",result); 26 27 }
4、其它還有很多,省略
Run-time Library Routines
- OpenMP API包含越來越多的運行時庫例程
- 對於C / C ++,所有運行時庫例程都是實際的子例程。對於Fortran,有些實際上是函數,有些是子例程。
- 對於C / C ++,通常需要包含 <omp.h>頭文件
例如:
1 #include <omp.h> 2 int omp_get_num_threads(void)
詳細的函數介紹可見OpenMP入門教程(二)
Environment Variables
- OpenMP提供一些環境變量來控制並行程序的執行
- 所有的環境變量名都是大寫字母,但是分配給它們的值不區分大小寫
1、OMP_NUM_THREADS:設置在運行期間最大的線程數
setenv OMP_NUM_THREADS 8
2、OMP_DYNAMIC:啟用或禁用動態調整可用於執行並行區域的線程數。有效值為TRUE或FALSE
setenv OMP_DYNAMIC TRUE
3、OMP_PROC_BIND:啟用或禁用綁定到處理器的線程。有效值為TRUE或FALSE。
setenv OMP_PROC_BIND TRUE
4、OMP_STACKSIZE:控制創建(非主)線程的堆棧大小
setenv OMP_STACKSIZE 2000500B setenv OMP_STACKSIZE“3000 k” setenv OMP_STACKSIZE 10M setenv OMP_STACKSIZE“10 M” setenv OMP_STACKSIZE“20 m” setenv OMP_STACKSIZE“1G” setenv OMP_STACKSIZE 20000
4、還有很多其它的,省略
注:前面的運行API也能做與環境變量一樣的工作,同時使用環境變量和運行時 API 會出現什么情況?運行時 API 將獲得更高的優先權。
注:這是一個簡單的OpenMP的練習網站:https://computing.llnl.gov/tutorials/openMP/exercise.html
參考鏈接:https://computing.llnl.gov/tutorials/openMP/#Abstract