并行程序设计导论pdf电子书_并行程序设计导论学习笔记——OpenMP(1)

本文介绍了如何在Visual Studio中启用OpenMP,重点讲解了OpenMP的关键函数omp_get_thread_num()和omp_get_num_threads(),以及#pragma omp parallel和#pragma omp parallel for指令。通过一个并行计算定积分的示例,展示了如何利用OpenMP进行任务分配和避免线程竞争。最终,文章讨论了并行算法的性能评估和阿姆达尔定律。
摘要由CSDN通过智能技术生成

使用OpenMP需要在编译器上打开OpenMP开关,并包含omp.h文件。我使用的是在Windows下的Visual Studio 2015,只需在工程选项中打开OpenMP支持就可以了。按照书上的说法,GCC增加参数-fopenmp就可以了。

OpenMP有两个重要的函数:

omp_get_thread_num()

omp_get_num_threads()

他们的返回值都是无符号整数,第一个用来返回当前执行的线程编号,而第二个返回一共有的线程数量。和C语言的数组下标类似,当前执行的线程编号的值从0开始,最大值总是线程数量减一。至于这两个函数的用途,是进行线程的任务分配。

#pragma omp为OpenMP的预处理器指令,他将告诉编译器的OpenMP的具体行为。

在5.1节中主要涉及到了OpenMP的#pragma omp parallel指令,用来将当前指令并行化。注意这个指令并不包括任何的任务分配,只是简单的将他下面的指令送给不同线程去执行而已。所以,简单的一条#pragma omp parallel指令下面接一个for循环,而for循环的起止点都是常数而不是根据当前线程分配的,只会把这个for循环用线程数量执行一遍,与我们预期的“将内容拆开,分配给不同线程”的想法相悖,无法起到并行化的效果。

书后面提到#pragma omp parallel for就可以自动分配线程任务,但是具体的分配方式还是取决于具体的控制语句。

对于OpenMP指令,还可以跟上一些从句(clause)。比如,在上面的#pragma omp parallel,可以跟上num_threads(thread_count),表示用thread_count的线程数量来运行。除此之外还有一条#pragma omp critical指令,用来在不同线程访问或者更新所有线程共享的资源(比如全局变量),用来防止竞争的情况(race condition)。

书上还说了一些概念性的东西:不同的线程是从进程forked出来的,共享大部分进程的资源(比如stdin,stdout,等),但是不同的线程享有不同的栈(stack)空间和程序计数器(program counter)。当一个线程执行完毕,将返回进程(的主线程)。不同的栈空间意味着局部变量是独立的,尽管可能是使用了同一个函数,但是他们的局部变量在OpenMP创建了子线程后依然是相互独立不相干的。

例题:用OpenMP实现定义法计算曲边梯形(定积分几何意义)的并行算法。

定义法计算定积分包括分割,求和,取极限三个步骤。串行算法比较容易实现,用循环累加分割的小区间的值就可以了。并行的时候,因为各个小区间求和互不相干(没有依赖性(dependence)和相互通信(communication)),因此只需要将不同小区间分配给不同线程就可以了。最后#pragma omp critical指令强调了汇总操作时只能由一个线程更新其共享资源(也相当于锁)。

例子的完整的实现如下:

#include#include /*clock() function*/#include#include /*OpenMP header*/#include

doubleresult_parallel;#define N 400000000

double get_approx_serial(double(*func)(double x), double interval_a, double interval_b, long long intn)

{doubleh;double result = 0;doublex_i;inti;

h= (interval_b - interval_a) /n;for (i = 0; i < n; i++)

{

x_i= interval_a + h*i;

result+= func(x_i)*h;

}returnresult;

}void get_approx_parallel(double(*func)(double x), double interval_a, double interval_b, long long intn)

{doubleh;double result = 0;doublex_i;long long intlocal_i_start, local_i_end;intthread_id, total_threads;inti;/*Get thread num*/total_threads=omp_get_num_threads();

thread_id=omp_get_thread_num();/*Task Arrangment between threads*/local_i_start= (n / total_threads)*thread_id;if (thread_id == total_threads - 1)

local_i_end=n;elselocal_i_end= (n / total_threads)*(thread_id + 1);/*approx Algorithm*/h= (interval_b - interval_a) /n;for (i = local_i_start; i < local_i_end; i++)

{

x_i= interval_a + h*i;

result+= func(x_i)*h;

}#pragma omp criticalresult_parallel+=result;return;

}intmain()

{

clock_t start_clock, end_clock;inttotal_thread_num;/*Get total threads to display*/

#pragma omp paralleltotal_thread_num=omp_get_num_threads();

printf("This computer Has %d Threads in total\n", total_thread_num);/*Do serial algorithm and get the used time.*/start_clock=clock();

printf("The area between y=sin(x) , y=0 , x=0 ,x=pi is %lf\n", get_approx_serial(&sin, 0, 3.141593, N));

end_clock=clock();

printf("Serial total use Time %d ms", end_clock -start_clock);/*Do parallel algorithm and get the used time.*/start_clock=clock();#pragma omp parallelget_approx_parallel(&sin, 0, 3.141593, N);

printf("\nThe area between y=sin(x) , y=0 , x=0 ,x=pi is %lf\n", result_parallel);

end_clock=clock();

printf("Parallel total use Time %d ms", end_clock -start_clock);

system("pause");return 0;

}

critical语句的反汇编情况如下,证明了编译器在解释这条语句时转换成了两个函数调用,来处理线程的竞争冲突。

程序的运行结果如下所示:我的机器是6核12线程的,所以一共有12条线程可以使用,并行算法比串行算法实际加速比(Speedup)$S=T_s/T_p=9912/984=10.07$,效率(efficiency)$E=(1/p)S=0.839$(计算方法和定义见书58页)。根据阿姆达尔定律(Amdahl's law) ,随着数据规模$N$的增大,串行代码占比几乎为零(只有#pragma omp critical子句声明的为串行代码),这个算法可以达到理论上几乎线性的加速比。实际上因为SMT超线程效率有限,加速比为并未达到12。

参考书籍:并行程序设计导论(英文版),机械工业出版社。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值