OpenMP 笔记

配置

正常现代编译器都支持OpenMP,以gcc为例只要添加-fopenmp参数就可启用openmp:

1
g++ test.cpp -fopenmp

在程序中检测是否开启openmp

1
2
3
#ifndef _OPENMP
std::cerr << "OpenMP not supported" << std::endl;
#endif

简单使用

只需要在for循环之前加上一句pragma就可实现并行化:

1
2
3
4
#pragma omp parallel for
for (int i = 0; i < 100; ++i) {
// do something
}

但对循环有五个要求否则编译将不通过:

  1. 循环变量必须是int
  2. 循环判断条件必须是<,> <=, >=
  3. 循环增量固定
  4. 如果比较符号为<,<=,则循环增量为正,反之亦然
  5. break、异常等只能在循环内部,总之程序执行流程不能跳出并行的for循环
    除此之外,不同迭代之间的数据不应该存在依赖。

公有和私有数据

for循环内定义的数据为openmp线程私有的数据,循环外为线程共享的数据。

1
2
3
4
5
6
int g_val = -1;
#pragma omp parrallel for private(g_val)
for (int i = 0; i < 100; ++i) {
cout << g_val << endl;
}
cout << g_val << endl;

上述程序中,通过private(g_val)将共有数据g_val私有化,具体地,每个线程都定义了一个g_val,并且未被初始化,所以循环内输出结果不等于-1,每个线程内的g_val与外部g_val无关。

1
#pragma omp parrallel for firstprivate(g_val)

firstprivate与private的区别在于每个线程的g_val会使用公有的g_val的初始化。

1
#pragma omp parrallel for lastprivate(g_val)

lastprivate会使用最后一次for循环迭代的g_val赋值给公有的g_val。

Reduction

1
2
3
4
5
6
int sum = 0;
#pragma omp parallel for reduction(+:sum)
for (int i = 0; i < 100; i++) {
sum += array[i];

}

上述程序中,openmp为每个线程定义了私有变量sum,并初始化为0,当线程退出时,会把每个线程内sum的值累加在一起赋值给循环外的sum。

Schedule

openmp使用下述语法配置线程调度:

1
#pragma omp parallel for schedule(kind [, size])

其中kind是调度类型,有static, dynamic, guided, runtime(由环境变量OMP_SCHEDULE来确定调度类型),size表示分配给线程的size次连续的迭代计算。