1. Introduction
寫 project euler 的時候, 有些問題想不到速度快的解法, 只好用平行運算來加速
平常程式都以一條 thread 在執行, 要開多條 thread 來跑, 在管理上就很麻煩
OpenMP 讓人可以平行處理, 又可以減少管理的麻煩
2. 對 for 做 parallel
底下是範例程式
#include <stdio.h> #include <omp.h> int main() { #pragma omp parallel for for (int i=0; i<6; i++) { printf("%d ", i); } printf("\r\n"); return 0; }然後compile它, 這邊使用c99來compile:
c99 -o openmp_example_01 openmp_example_01.c -fopenmp
幾個重點
- 在 header 檔要加入 omp.h
- 在 for 迴圈前加上 #pragma omp, 這個是用來使用omp的功能, 後面是控制敘述
- compile 的時候加上 -fopenmp
4 0 1 5 2 3
每次執行的結果都不一樣
3. 語法
它的語法大改長這樣
#pragma omp <directive> [clause[[,] clause] ...]
前個例子裡, 其實 parallel 和 for 都是 directive 的敘述, 所以它可以拆成兩個
#pragma omp parallel { #pragma omp for for (int i=0; i<6; i++) { printf("%d ", i); } }可以看 wiki 上面的指令清單
OpenMP in wiki
4. thread information
如果要知道目前的 thread number, 可以用 omp_get_thread_num()
#include <stdio.h> #include <omp.h> int main() { #pragma omp parallel for for (int i=0; i<6; i++) { printf("T:%d i:%d\r\n", omp_get_thread_num(), i ); } return 0; }
執行結果
T:2 i:4 T:1 i:2 T:1 i:3 T:0 i:0 T:0 i:1 T:3 i:5
可以看到我的電腦上, 它跑了4條thread, 其中 thread 0處理 i 是 0 & 1的情況, thread 1 處理 i 是 2 & 3 的情況....
5. 控制 thread number
可以用 num_threads(n) 來控制要開多少 thread
#include <stdio.h> #include <omp.h> int main() { #pragma omp parallel for num_threads(2) for (int i=0; i<6; i++) { printf("T:%d i:%d\r\n", omp_get_thread_num(), i ); } return 0; }
執行結果可以看到只開2條thread了
T:1 i:3 T:1 i:4 T:1 i:5 T:0 i:0 T:0 i:1 T:0 i:2
6. 手動切割 section 做平行處理
#include <stdio.h> #include <omp.h> int main() { #pragma omp parallel sections { #pragma omp section { printf("T:%d section 0\r\n", omp_get_thread_num()); } #pragma omp section { printf("T:%d section 1\r\n", omp_get_thread_num()); } #pragma omp section { printf("T:%d section 2\r\n", omp_get_thread_num()); } } return 0; }
執行結果
T:2 section 0 T:3 section 1 T:0 section 2
要注意的是 section 之間不可以有相依性, 不然要額外做處理
7. for 裡面處理共同變數
要注意的是 parallel for 只會將 section 裡的變數做平行處理,
section 外的變數則會當成是所有thread 共用的變數,
要讓 openmp知道 section 裡的變數是每個thread自己有一份的話, 則要加上private
#include <stdio.h> #include <omp.h> int main() { int i, j; #pragma omp parallel for private ( j ) for (i=0; i<2; i++) { for (j=0; j<2; j++) { printf("(%d,%d)\r\n", i, j); } } return 0; }
如果不加上private, 那麼當外面的 thread 在做處理時,
就有可能不小心把別的 thread 的變數 j 加了 1
如果希望 section 裡面每個 thread 共用一份變數,
並且希望它不會有處理先後順序造成的問題, 可以使用 atomic
#include <stdio.h> #include <omp.h> int main() { int count = 0; #pragma omp parallel for for (int i=0; i<10; i++) { #pragma omp atomic count++; } printf("count: %d\r\n", count); return 0; }如果沒有加上 atomic 的話, count 最後的結果就有可能不是10