270 likes | 457 Views
OpenMP 范例. OpenMP 范例. 夏霏 2009.12. ?. 1. 3. 2. OpenMP. Why OpenMP. CPU 核数扩展性. 方便性. 可移植性. OpenMP 例子. 反依赖的消除 循环依赖的消除 OpenMP 实现流水线. 反依赖的例子. int V[ VSIZE+1 ],i,U[VSIZE+1]; for (i=0; i<VSIZE+1; i++) { V[i]= i ; U[i] = i ; }
E N D
OpenMP 范例 OpenMP 范例 夏霏 2009.12
? 1 3 2 OpenMP Why OpenMP CPU核数扩展性 方便性 可移植性
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 for • OpenMP指令使用的格式为 • #pragma omp 指令 [子句[子句]…] • parallel for就是一条指令,“指令” = “编译指导语句” private 子句
parallel for • parallel指令的用法 • parallel 是用来构造一个并行块的,也可以使用其他指令如for、sections等和它配合使用。 • #pragma omp parallel [for | sections] [子句[子句]…] • { • //代码 • }
parallel for • for指令的使用方法 • 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]$
并行程序设计 高性能 依赖 计算任务划分 死锁 通讯
延伸阅读 【1】 《多核计算与程序设计》 作 者: 周伟明 著 出 版 社: 华中科技大学出版社 第三章中关于OpenMP相关内容 【2】 Barbara Chapman, “How OpenMP is Compiled ” http://cobweb.ecn.purdue.edu/ParaMount/iwomp2008/documents/chapman-underthehood
谢谢大家 谢谢大家 夏霏 2009.12