高性能计算之OpenMP——超算习堂学习2
OpenMP学习2——超算习堂
一、for指令的使用方法细嚼
1.1、parallel for指令的用法
在OpenMP并行程序设计中,for循环是一种独立的并行指令。它非常重要!它的指令格式是:
#include<omp.h>#pragmaomp parallelforfor(i=begin;i<end;++i){// Content}十分需要注意的是:
parallel for指令的后面必须要紧跟for语句块!!!!
并且for循环并行必须要处在parallel并行区块内!!!!否则会当作串行执行!
1.2、parallel for指令的执行机制
此前一篇博客已经说明了,OpenMP的并行计算模式是插入并行语句的方法,如上图。当我们的串行程序执行到并行语句块的时候,会从主线程中派生出线程组,然后线程组对计算任务进行均分,然后并行计算。并行计算结束后重新回到串行程序。
1.3、parallel for并行程序设计案例
案例1:计算两个向量的点乘
#include<omp.h>#include<cstdio>#include<cstdlib>#include<cmath>#include<ctime>constintmaxn=5e7;constintmod=10000;intvec1[maxn],vec2[maxn],vec[maxn],i;intmain(){srand((unsignedint)time(NULL));for(i=0;i<maxn;++i){vec1[i]=rand()%mod;vec2[i]=rand()%mod;}printf("--------------before parallel compute---------------\n");clock_t s,t;s=clock();for(i=0;i<maxn;++i){vec[i]=vec1[i]*vec2[i];}t=clock();printf("--------------used time = %d ms---------------\n",t-s);s=clock();printf("--------------enter parallel compute---------------\n");#pragmaomp parallelnum_threads(20)shared(vec1,vec2,vec)private(i){#pragmaompforfor(i=0;i<maxn;++i){vec[i]=vec1[i]*vec2[i];}}t=clock();printf("--------------used time = %d ms---------------\n",t-s);return0;}计算效率对比:
案例2:parallel for并行计算矩阵乘法
#include<omp.h>#include<cstdio>#include<cstdlib>#include<cmath>#include<ctime>constintmaxn=1000;constintmod=10000;intvec1[maxn][maxn],vec2[maxn][maxn],vec[maxn][maxn],i,j,k;intmain(){srand((unsignedint)time(NULL));for(i=0;i<maxn;++i){for(j=0;j<maxn;++j){vec1[i][j]=rand()%mod;vec2[i][j]=rand()%mod;}}printf("--------------before parallel compute---------------\n");clock_t s1,t1,s2,t2;s1=clock();for(i=0;i<maxn;++i){for(j=0;j<maxn;++j){for(k=0;k<maxn;++k){vec[i][j]+=(vec1[i][k]*vec2[k][j]);}}}t1=clock();printf("----------------used time = %d ms-----------------\n",t1-s1);printf("--------------enter parallel compute---------------\n");s2=clock();#pragmaomp parallelforcollapse(2)schedule(dynamic)private(i,j,k)shared(vec1,vec2,vec)for(i=0;i<maxn;++i){for(j=0;j<maxn;++j){for(k=0;k<maxn;++k){vec[i][j]+=(vec1[i][k]*vec2[k][j]);}}}t2=clock();printf("----------------used time = %d ms-----------------\n",t2-s2);printf("\n----------------the speedup ratio = %lf---------------\n",1.0*(t1-s1)/(t2-s2));return0;}运行结果如下:
1.4、运用omp parallel for的一些注意事项
第一:omp parallel for 和 omp for 不要混用
一旦在前面有使用过
#pragmaomp parallel...语句,并且当前还处在这个并行区,然后这个时候你想使用for循环并行,则千万不要再搞一次:
#pragmaomp parallelfor这样的操作了,因为这样会让线程组重组,然后相当于有两重并行,举个例子看看:
在有#pragma omp parallel 的并行区下运行
#include<omp.h>#include<iostream>usingnamespacestd;intmain(){#pragmaomp parallelnum_threads(10){#pragmaompforfor(inti=0;i<5;++i){#pragmaomp critical{cout<<"i = "<<i<<endl;}}}return0;}此时的运行结果是:
然后如果你多此一举多搞一遍parallel命令就会:
#include<omp.h>#include<iostream>usingnamespacestd;intmain(){#pragmaomp parallelnum_threads(4){#pragmaomp parallelfor// 注意看这里哦for(inti=0;i<5;++i){#pragmaomp critical{cout<<"i = "<<i<<endl;}}}return0;}看看这样的“多此一举”的运行结果:
我们会发现,这个0 ~ 4 被重复执行啦!这样会影响并行程序结果,还会误以为运行的开销变大哦!!!!
造成这种结果的原因就是:parallel命令会告诉操作系统,此时我要重组线程组,要重新开始并行程序运行。然后这下好啦,每个线程到了那句指令的时候都重组线程组,白白多执行4次(取决于线程数)
二、多线程下数据访问同步
2.1、同步一词在并行计算的含义
其实同步啊,在并行计算里有两种含义:
第一:线程/进程的运行有的快有的慢,我想要在某处各个线程/进程达到同样的状态,这叫并行程序的运行同步
第二:对于共享内存的模型,我们需要控制数据的访问,达到线程同步。这样做的目的是为了防止多个进程/线程同时访问某个数据、内存,导致该数据同时改变,这样的作用下会让数据失真!举个例子:初始有变量a = 2,比如线程A要让a++,线程B要让a*=2。如果不控制访问,让变量a(或者某语句块)的执行的时候只能让一个线程进入执行,其他线程等待执行。则会让资源出现同步问题。这就叫数据同步。
2.2用critical创建临界区的方式避免线程同步危害
#include<omp.h>#include<iostream>usingnamespacestd;intmain(){inti,len,cnt=0;#pragmaomp parallelnum_threads(6){len=omp_get_num_threads();#pragmaompforprivate(i)for(i=0;i<len;++i){#pragmaomp critical{cout<<"Current is "<<i<<endl;cnt+=i;}}}cout<<"cnt = "<<cnt<<endl;return0;}