OpenMP入門教程(三)
承接前面兩篇,這裡直接逐一介紹和使用有關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