1. 简介
OpenMP是一个编译器指令和库函数集合,主要是为共享式存储计算机并行程序设计使用的。
OpenMP的一个Parallelfor指令,就是标准的并行模式fork/join式并行模式,基本思想是,程序开始时只有一个主线程,程序中的串行部分都由主线程执行,并行的部分是通过派生其他线程来执行,但是如果并行部分没有结束时是不会执行串行部分的。也即OpenMP并行执行的程序要全部结束后才能执行后面的非并行部分。
2. 调用形式:#pragma omp 指令 【子句【子句】…】
①#pragma omp parallel
{
每个线程都会执行大括号里的代码,线程id号不确定
}
带有for语句:将for循环拆分开来尽可能平均地分配到各个线程执行,要求数据不存在依赖。
1)#pragma omp parallel for
for()
作用域只是紧跟着的那个for循环,紧跟着的for变成并行,要求里面的循环互不干扰。
#pragma ompparallel for
for (int i = 0; i < 6; i++)
printf("i = %d, I amThread %d\n", i, omp_get_thread_num());
//这里是两个for循环之间的代码,将会由线程0即主线程执行
printf("I am Thread %d\n", omp_get_thread_num());
#pragma ompparallel for
for (int i = 0; i < 6; i++)
printf("i = %d, I amThread %d\n", i, omp_get_thread_num());
注意:两个for循环之间有一些代码只能有一个线程执行
2)#pragma omp parallel
{ //注意:大括号必须要另起一行
#pragma omp for
for()
}
整个并行块中可以出现多个for指令
#pragma ompparallel
{
#pragma omp for
for (int i = 0; i < 6;i++)
printf("i = %d,I am Thread %d\n", i, omp_get_thread_num());
#pragma omp master
{
//这里的代码由主线程执行,改成single是随机选一个线程执行
printf("I amThread %d\n", omp_get_thread_num());
}
#pragma omp for
for (int i = 0; i < 6;i++)
printf("i = %d,I am Thread %d\n", i, omp_get_thread_num());
}
for循环是一个一个平均分配给各个线程
②指定执行的线程数
#pragma ompparallel num_threads(8)
{//花括号需换行
}
③不同段并行执行
#pragma ompparallel sections
{ //需要保证各section里的代码执行时间相差不大
#pragma omp section
{
}
}
omp_get_num_procs()函数来获取处理器个数,用omp_get_thread_num()函数来获得每个线程的ID,为了使用这两个函数,我们需要include
3.数据同步
(默认情况下sum变量是每个线程共享的,所以多个线程同时对sum操作时就会因为数据同步问题导致结果不对)
主要有循环里涉及到求和,或者求最大值的。
1)求和
#pragmaomp for reduction(+:sum)//归约(reduction),它只支持一些基本操作,比如+,-,*,&,|,&&,||等
for(int i = 0; i < n; i++) {
sum+= 1;
}// 每个线程拷贝一份sum变量,退出并行块时再把各个线程的sum相加
或者
#pragma omp parallel{
#pragma omp for
for(int i = 0; i < n; i++) {
{
#pragmaomp critical
sum+= 1;
}
}
}
cout<< " sum = " << sum << endl;
2)求最大值
#pragma omp parallel for
for(int i=0;i<10;i++)
{
inttemp = a[i];
#pragma omp critical//执行到critical里面时,要注意有没有其他线程正在里面执行,如果有的话,要等其他线程执行完再进去执行。 比较慢
if(temp > max)
max= temp;
}
3)用类似于互斥量的机制进行私有化和同步,可以消除数据竞争。
#pragma omp parallel for private(x)
for(i=0;i<80;i++)
{
x=sin(i);
if(x>0.6)x=0.6;
printf("sin(%d)=%f\n",i,x);
}
4.其他语句
schedule语句(把循环一块一块分配给线程)
#pragma omp parallel
{
#pragma omp for schedule(static, 3)
//每一块大小为3,然后再平均分配给各个线程执行。
for(int i = 0; i < 12; i++) {
{
printf("i= %d, I am Thread %d\n", i, omp_get_thread_num());
}
}
}
结果:线程0执行i=0 1 2,线程1执行i=3 4 5,线程2执行i=6 7 8,线程3执行i=9 1011,如果后面还有则又从线程0开始分配。先后顺序不一定。
schedule的类型一共是三种:static、dynamic、guided。
a. 静态:OpenMP会给每个线程分配size次迭代计算。这个分配跟实际的运行无关,可以从逻辑上推断出哪几次迭代会在哪几个线程上运行。各个线程的任务是固定的,但是可能有的快,有的慢,不能达到最优。
b.动态:谁有空,给谁分配一次迭代让它去跑。每个线程按先执行完先分配的方式执行1次循环(有size参数就先执行size次)。根据线程的执行快慢,已经完成任务的线程会自动请求新的任务或者任务块,每次领取的任务块是固定的。
C,类似于动态调度,但每次分配的循环次数不同,开始比较大,以后逐渐减小。size表示每次分配的迭代次数的最小值,由于每次分配的迭代次数会逐渐减少,较少到size时,将不再减少。每个任务分配的任务是先大后小,指数下降。当有大量任务需要循环时,刚开始为线程分配大量任务,最后任务不多时,给每个线程少量任务,可以达到线程任务均衡。
语句barrier,用它可以在并行块中设置一个路障,必须等待所有线程到达时才能通过,这个一般在并行处理循环前后存在依赖的任务时使用到 。
私有变量private,firstprivate,lastprivate,threadprivate的使用:
http://syyming.blog.163.com/blog/static/2320635201082434535924/
OpenMP其他语句:http://blog.csdn.net/drzhouweiming/article/details/1175848
5. 实际操作中发现的问题
1. 其实只要会使用基本的#pragma omp parallelfor就可以了,要注意变量的定义(若循环中的该变量一直在变,则把定义放在循环内,即每个循环都创建一次)。
2. 需要并行的for循环的终止条件不能用!=
3. error C3037: “reduction”子句中的变量必须在封闭上下文中共享
GetSaliencyMap
4. 涉及并行的中断
http://blog.csdn.net/yongh701/article/details/51395628
要求在i=100时退出,【for(inti=0;i<200&&(i!=0&&i%100==0);i++) 会显示errorC3017: OpenMP“for”语句中的终止测试格式不正确。】
需添加标志位;
bool finish_flag=false;
#pragma omp parallel fornum_threads(4)
for(i=0;i<200;i++){
if(finish_flag==true){
continue;
}
if(finish_flag==false){
if(i!=0&&i%100==0){
result=i;
finish_flag=true;
}
}
}
cout<
并发与并行:
并发(concurrency):指一个处理器同时处理多个任务。指在同一时刻只能有一条指令执行,但多个进程指令被快速的轮换执行,即把时间分成若干段,使多个进程快速交替的执行。
并行(parallel):指多个处理器或者是多核的处理器同时处理多个不同的任务。在同一时刻,有多条指令在多个处理器上同时执行。
参考:
http://blog.csdn.net/gengshenghong/article/details/7000979
http://blog.csdn.net/lanbing510/article/details/17108451
http://blog.sina.com.cn/s/blog_66474b160100z15b.html
http://www.cnblogs.com/xudong-bupt/p/3622101.html
https://www.cnblogs.com/hantan2008/p/5961312.html