(done) 并行计算学习 (Day1: 两个简单的 OpenMP 例子)
来源:https://dazuozcy.github.io/posts/introdution-to-openmp-intel/#1-%E6%A6%82%E8%BF%B0
一个比较有意义的模型
Master thread 在经过 parallel Task1 后也仅仅只保留 Master thread。
或者说,在进入 Fork 阶段之前,以及进入 Join 阶段时,都只有 Master thread 运行
直接看练习
练习1:并行化 Hello World
#include <stdio.h>
#include <omp.h>
int main()
{
#pragma omp parallel
{
int ID = omp_get_thread_num();
printf("hello(%d)", ID);
printf(" world(%d) \n", ID);
}
return 0;
}
使用 OpenMP 编译上面的 Hello World C 文件
gcc -fopenmp foo.c
export OMP_NUM_THREADS=4
./a.out
#pragma omp parallel 申请默认数量的线程。 omp_get_thread_num() 返回每个线程的唯一标识。范围是[0,N]。
这里我遇到过错误信息:“error: invalid branch to/from OpenMP structured block”
该错误信息通常出现在使用 OpenMP 并行编程模型时,特别是在处理并行区域的分支(例如 if 或 goto)时出现了问题。为了理解这个错误,首先需要了解 OpenMP 并行区域的基本工作方式。
OpenMP 中的 结构化块(structured blocks)是指在并行区域中使用的控制流结构,例如循环(for)、并行区域(parallel)、并行部分(sections)等。结构化块有严格的规则,以便 OpenMP 能正确地对代码进行并行化处理。
该错误的含义是 尝试在 OpenMP 的结构化块中进行不允许的跳转或分支操作。以下是可能引发该错误的情况:
- 从 OpenMP 结构化块跳出或跳入:在 OpenMP 的结构化块(如 parallel、for、sections)内,如果有跳转语句(例如 goto 或 break),可能会被认为是不合法的。OpenMP 要求这些结构化块中的控制流是受控的,因此不允许跳转。
- 条件分支(if)的使用不当:某些情况下,在 OpenMP 并行区域中使用 if 语句时可能会导致这个错误,尤其是当 if 语句导致跳转到不允许的地方时。例如,如果你在并行区域内根据某个条件来跳转到外部的代码区域,而不是保持结构化代码块的完整性,就可能触发这个错误。
练习2:把下面这个串行版本的计算Pi值的程序改成并行版本。
void calc_pi_serial()
{
long num_steps = 0x20000000;
double sum = 0.0;
double step = 1.0 / (double)num_steps;
// 圆周率计算原理:圆周率的积分公式
// 使用梯形法逼近积分的值
double start = omp_get_wtime( );
for (long i = 0; i < num_steps; i++) {
double x = (i + 0.5) * step;
sum += 4.0 / (1.0 + x * x);
}
double pi = sum * step;
printf("pi: %.16g in %.16g secs\n", pi, omp_get_wtime() - start);
// will print "pi: 3.141592653589428 in 5.664520263002487 secs"
}
给了一个修改为并行的例子:
#define NUM_THREADS 2
void calc_pi_omp_v1()
{
long num_steps = 0x20000000;
double sum[NUM_THREADS] = { 0.0 };
double step = 1.0 / (double)num_steps;
int nthreads;
double start = omp_get_wtime( );
omp_set_num_threads(NUM_THREADS);
#pragma omp parallel
{
int id = omp_get_thread_num();
int nthrds = omp_get_num_threads();
if (id == 0) { // master thread
nthreads = nthrds;
}
for (long i = id; i < num_steps; i += nthrds) {
double x = (i + 0.5) * step;
sum[id] += 4.0 / (1.0 + x * x);
}
}
double pi = 0;
for (int i = 0; i < nthreads; i++) {
pi += sum[i]*step;
}
printf("pi: %.16g in %.16g secs\n", pi, omp_get_wtime() - start);
}
上述并行版本可以得到正确的结果,但是当NUM_THREADS的值配置的更大的时候,耗时反而增加了。
原因是伪共享(false sharing)。