2015/04/30

OpenMP in linux

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