Download - 夏霏 2009.12
夏霏2009.12
?
Why OpenMP
1
CPU 核数扩展性
3
可移植性
2
方便性
OpenMP
OpenMP 例子
• 反依赖的消除
• 循环依赖的消除
• OpenMP 实现流水线
反依赖的例子 int V[ VSIZE+1 ],i,U[VSIZE+1];
for (i=0; i<VSIZE+1; i++) {
V[i]= i ;
U[i] = i ;
}
for(i=0;i<VSIZE;i++) U[i] = U[i] + U[i+1];
omp_set_num_threads(NUM_THREADS);
#pragma omp parallel for default(none) shared(V) private(i) schedule(static)
for (i=0; i<VSIZE; i++) {
V[i] = V[i] + V[i+1];}
parallel forOpenMP 指令使用的格式为 # pragma omp 指令 [ 子句 [ 子句 ]…]
parallel for 就是一条指令,“指令” = “ 编译指导语句” private 子句
#pragma omp directive-name [clause, ...] newline
制导指令前缀。对所有的OpenMP 语句都需要这样的前缀。
OpenMP 制导指令。在制导指令前缀和子句之间必须有一个正确的 OpenMP 制导指令。
子句。在没有其它约束条件下,子句可以无序,也可以任意的选择。 这一部分也可以没有。
换行符。表明这条制导语句的终止。
parallel forparallel 指令的用法
parallel 是用来构造一个并行块的,也可以使用其他指令如 for 、 sections 等和它配合使用。
#pragma omp parallel [for | sections] [ 子句 [ 子句 ]…]
{
// 代码}
parallel forfor 指令的使用方法
for 指令则是用来将一个 for 循环分配到多个线程中执行。 for 指令一般可以和 parallel 指令合起来形成 parallel for 指令使用,也可以单独用在 parallel 语句的并行块中。
#pragma omp [parallel] for [ 子句 ]
for 循环语句
parallel for• parallel ,用在一个代码段之前,表示这段代码将被多
个线程并行执行
• for ,用于 for 循环之前,将循环分配到多个线程中并行执行,必须保证每次循环之间无相关性。
• parallel for , parallel 和 for 语句的结合,也是用在一个 for 循环之前,表示 for 循环的代码将被多个线程并行执行。 – SIMD
反依赖的例子"badloop.c "
int V[ VSIZE+1 ],i,U[VSIZE+1];
for (i=0; i<VSIZE+1; i++) {
V[i]= i ;
U[i] = i ;
}
for(i=0;i<VSIZE;i++) U[i] = U[i] + U[i+1];
omp_set_num_threads(NUM_THREADS);
#pragma omp parallel for default(none) shared(V) private(i) schedule(static)
for (i=0; i<VSIZE; i++) {
V[i] = V[i] + V[i+1]; // 先读后写的反依赖关系}
反依赖处理 1
Parallelizing an inner loop with dependences
Backward dependency
for (i=0; i<size-1; i++) {
V[i] = V[i]+V[i+1];
}
Method: Eliminate dependences by duplicating data
Optimization: None, duplicate the full data
对引起反依赖关系的 V[i+1] 采用了数据冗余的作法,引入oldV 数组,从而解决先读后写的反依赖关系。
反依赖处理 1"loopA1.c"
#pragma omp parallel for default(none) shared(V,oldV) private(i) schedule(static)
for (i=0; i<VSIZE; i++) {oldV[i] = V[i] ;}
#pragma omp parallel for default(none) shared(V,oldV) private(i) schedule(static)
for (i=0; i<VSIZE-1; i++) {V[i] = V[i]+oldV[i+1];}
反依赖处理 2
• 采用的边界范围 limitL 和 limitR ,以及边界值 border ;首先,各线程读取各自的border (编号最大的一个线程出外),然后同步。
• 接着,各线程独立更新 limitL ~ limitR – 1 范围内的值,再更新 limitR 的值;再次同步!
反依赖处理 2"loopA2.c"
#pragma omp parallel default(none) shared(V,size) private(id,LimitL,LimitR,border,i)
{
id = omp_get_thread_num();
LimitL = id*size;LimitR = (id+1)*size-1; // 确定边界
if (id != THREADS_NUM-1) border = V[LimitR+1];
#pragma omp barrier
...
反依赖处理 2
... ...
for (i=LimitL; i<LimitR; i++) {
V[i] = V[i] + V[i+1];
}
if (id != THREADS_NUM-1)
V[LimitR] = V[LimitR] + border ;
#pragma omp barrier
barrier ,用于并行区内代码的线程同步,所有线程执行到 barrier 时要停止,直到所有线程都执行到 barrier 时才继续往下执行。
OpenMP 例子
• 反依赖的消除
• 循环依赖的消除
• OpenMP 实现流水线
循环分布变换的例子"dis-err.c"
int a[Iter],b[Iter],c[Iter],d[Iter],f[Iter];
int a1[Iter],b1[Iter],c1[Iter],d1[Iter],f1[Iter];
for(i=0;i<Iter;i++) a[i] = b[i] = c[i] = d[i] = f[i] =i;
for(i=0;i<Iter;i++) a1[i] = b1[i] = c1[i] = d1[i] = f1[i] =i;
循环分布变换的例子 for(i=4;i<Iter-1;i++){
a1[ i ] = b1[ i-2 ] + 1; c1[ i ] = b1[ i-1 ] + f1[ i ]; b1[ i ] = a1[ i-1 ] + 2; d1[ i ] = d1[ i+1] + b1[ i-1]; }
#pragma omp parallel for shared(a,b,c,d,f) private(i) for(i=4;i<Iter-1;i++){
a[ i ] = b[ i-2 ] + 1; c[ i ] = b[ i-1 ] + f [ i ]; b[ i ] = a[ i-1 ] + 2; d[ i ] = d[ i+1 ] + b[ i-1 ]; } //ERROR
循环分布变换的例子
• "dis-ok1.c"– S1: a[ i ] = b[ i-2] + 1;– S2: c[ i ] = b[ i-1] + f[ i ]; – S3: b[ i ] = a[ i-1] + 2; – S4: d[ i ] = d[ i+1] + b[ i-1];
• int old_d[Iter]; // duplicating array-d to avoid anti-dependency
循环分布变换的例子#pragma omp parallel shared(a,b,c,d,old_d,f) private(i){ #pragma omp master for(i=4;i<Iter-1;i++){ a[i] = b[i-2] + 1; // S1 b[i] = a[i-1] + 2; // S3 } // 只有主线程执行循环 S1 和 S3
#pragma omp barrier
/* The Parallel Loop below containing both Statement S2 and S4*/ #pragma omp for for(i=4;i<Iter-1;i++) { c[i] = b[i-1] + f[i] ; // S2 d[i] = old_d[i+1] + b[i-1] ; // S4 } // 消除反依赖后, S2 和 S4 由其他线程并行执行
OpenMP 例子
• 反依赖的消除
• 循环依赖的消除
• OpenMP 实现流水线
使用 OpenMP 实现流水线for (iter=0; iter<numiter; iter++)
{for (i=0; i<size-1; i++){
V[i] = f( V[i], V[i – 1 ] ); } }
流依赖! 内层循环无法直接采用 OMP 制导命令来并行化
for (j=0; j<M; j++)
#pragma omp parallel for default(none) shared(V) private(i) schedule(static)
for (i=1; i<N; i++) V[i] = ( V[i] + V[i-1] ) / 2 ; ERROR
使用 OpenMP 实现流水线鉴于程序的特点,可以施加流水线并行技术。Program a threads pipeline !!并行域,流水线核心:
// Parallel Region // 每个线程进行 M + nthreads 次迭代以完成流水线 M+nthreads-1
1). 拷贝邻居线程的边界数据 border = V[limitL - 1]2). 在更新局部数据前,所有线程同步 barrier
3). 以流水线方式计算局部数据更新3a). 每个线程计算自己的第一个元素的值a[limitL] = ( a[limitL] + border ) / 2;3b). 计算剩余元素的值for (i=limitL+1; i<=limitR; i++) a[i] = ( a[i] + a[i-1] ) / 2;
// 拷贝已更新的边界数据前,所有线程再次同步。之后回到 1)//End of Parallel Region
使用 OpenMP 实现流水线• M = 2 , nthreads = 3• 黑色表示线程 #0 ,红色表示线程 #1 ,黄色表示线程 #2
使用 OpenMP 实现流水线以下为实际程序的在 1 、 2 、 4 、 8 个线程下实际运行结果。登录结点 43 后,可以在 rsh 到结点 28 上(可能负荷轻点):
[alex@node28 omp-demo]$ export OMP_NUM_THREADS=1[alex@node28 omp-demo]$ ./pipe 1000000 1000 1 Threads of 1000 iterations with 1000000 elements = 9.445195 (sec)[alex@node28 omp-demo]$ export OMP_NUM_THREADS=2[alex@node28 omp-demo]$ ./pipe 1000000 1000 2 Threads of 1000 iterations with 1000000 elements = 4.814943 (sec)[alex@node28 omp-demo]$ export OMP_NUM_THREADS=4[alex@node28 omp-demo]$ ./pipe 1000000 1000 4 Threads of 1000 iterations with 1000000 elements = 2.571246 (sec)[alex@node28 omp-demo]$ export OMP_NUM_THREADS=8[alex@node28 omp-demo]$ ./pipe 1000000 1000 8 Threads of 1000 iterations with 1000000 elements = 1.176101 (sec)[alex@node28 omp-demo]$
并行程序设计高性能
计算任务划分 依赖
死锁通讯
延伸阅读
《多核计算与程序设计》作 者: 周伟明 著出 版 社: 华中科技大学出版社
第三章中关于 OpenMP 相关内容
【 1 】
【 2 】 Barbara Chapman , “ How OpenMP is Compiled ”http://cobweb.ecn.purdue.edu/ParaMount/iwomp2008/documents/chapman-underthehood
夏霏2009.12