OpenMP中不建议使用并行嵌套,如果一个并行计算中的某个线程遇到了另外一个并行分支,程序运行将会变得不稳定。将一个完整的工作任务通过一组并行线程分成若干小任务,每个线程只执行指定给它的那段代码,并没用多余的线程去做其他的工作,即使并行计算中正在运行的某个线程遇到了一个新的并行分支,通过分割这个任务形成更多的线程,这并没有任何实际意义。因此,嵌套并行在OpenMP中将不考虑。OpenMP在处理多级并行嵌套时默认采用串行的执行方式,所以采用多级的并行在程序执行上并不会获得更高的计算效率。
下面通过一个例子来说明OpenMP中的并行嵌套。代码如下:
- //File :NestingTest.cpp
- #include"stdafx.h"
- #include<omp.h>
- #include<iostream>
- using namespace std;
- void NestingTest()
- {
- int i,j;
- #pragma omp parallel for
- for(i=0;i<4;i++)
- {
- #pragma omp parallel for
- for(j=0;j<4;j++)
- {
- cout<<"j Threads:"<<omp_get_num_threads()<<" ThreadID:"<<omp_get_thread_num()<<"\n";
- }
- cout<<" i="<<i<<" Threads:"<<omp_get_num_threads()<<"\n";
- }
- }
这是一个简单的并行嵌套,运行结果如下:
j Threads:j Threads:1
ThreadID:01 ThreadID:0
j Threads:1 ThreadID:0
j Threads:1 ThreadID:0
j Threads:1 ThreadID:0
j Threads:1 ThreadID:0
j Threads:1 ThreadID:0
j Threads:1 ThreadID:0
i=2 Threads:8
j Threads:1 ThreadID:0
j Threads:1 ThreadID:0
j Threads:1 ThreadID:0
j Threads:1 ThreadID:0
j Threads:1 ThreadID:0
j Threads:1 ThreadID:0
j Threads:1 ThreadID:0
i=3 Threads:8
i=0 Threads:8
j Threads:1 ThreadID:0
i=1 Threads:8
从输出结果可以发现,在i的循环中循环之间处于并行状态,而在j中循环属于串行。默认情况下即使将j循环定义成并行,其内部执行时仍然按串行方式运行。
若需要使用嵌套并行,则需要使用 omp_set_nested() 函数设置在程序中可用并行嵌套。如下例子:
- //File :NestedTest.cpp
- #include"stdafx.h"
- #include<omp.h>
- #include<iostream>
- using namespace std;
- void NestedTest()
- {
- int i,j,k;
- k=omp_get_nested();
- cout<<"是否支持并行嵌套:"<<k<<"\n";
- omp_set_nested(1);//设置支持嵌套并行
- k=omp_get_nested();
- cout<<"是否支持并行嵌套:"<<k<<"\n";
- double starttime,endtime;
- starttime=omp_get_wtime();
- #pragma omp parallel for
- for(i=0;i<2;i++)
- {
- #pragma omp parallel for
- for(j=0;j<4;j++)
- {
- cout<<" i="<<i<<" j="<<j<<" Threads:"<<omp_get_num_threads()<<" ThreadID:"<<omp_get_thread_num()<<"\n";
- }
- cout<<" I="<<i<<" Threads:"<<omp_get_num_threads()<<" ThreadID:"<<omp_get_thread_num()<<"\n";
- }
- endtime=omp_get_wtime();
- k=omp_get_nested();
- cout<<"是否支持并行嵌套:"<<k<<"\n";
- cout<<"计算耗时:"<<endtime-starttime<<"s\n";
- }
运行程序,其结果如下:
是否支持并行嵌套:0
是否支持并行嵌套:1
i= i=1 j=2 Threads:8 i=0 j=0 Threads:8 ThreadID:0
ThreadID:2
i=0 j=3 Threads:8 ThreadID:3
i=0 j=1 Threads:8 ThreadID:1
i=1 j=3 Threads:8 ThreadID:3
i=1 j=1 Threads:8 ThreadID:1
0 j=2 Threads:8 ThreadID:2
i=1 j=0 Threads:8 ThreadID:0
I=0 Threads:8 ThreadID:0
I=1 Threads:8 ThreadID:1
是否支持并行嵌套:1
计算耗时:0.0338853s
从上面结果可见,不仅在i的循环中每个循环处于并行,在j中每个循环也处于并行状态。默认情况下,程序的并行嵌套是不可用的,即并行中的并行将会当作串行来处理。如果使用omp_set_nested()设置其可用并行嵌套,则当并行中的线程遇到新的并行时,会创建新的线程来并行处理。
下面用一个例子来比较嵌套并行与非嵌套并行的运行效率,代码如下:
- //File :NestingTest02.cpp
- #include"stdafx.h"
- #include<omp.h>
- #include<iostream>
- using namespace std;
- void test()
- {
- int i;
- for(i=0;i<100000000;i++);
- }
- void NestingTest02()
- {
- int i,j;
- double starttime,endtime;
- starttime=omp_get_wtime();
- #pragma omp parallel for
- for(i=0;i<8;i++)
- {
- #pragma omp parallel for
- for(j=0;j<8;j++)
- {
- test();
- }
- }
- endtime=omp_get_wtime();
- cout<<"计算耗时:"<<endtime-starttime<<"s\n";
- }
上面代码是一个嵌套并行,其计算耗时约为2.63688s。如果将j循环上面的并行指令注释,只有一个并行结构,其结果为0.678552s。如果将i循环上面的并行指令注释,保留j循环前面的并行指令,其结果为2.67237s。如果都不采用并行,直接用串行运行,其计算耗时为16.1329s。可以,采用嵌套并行并不一定能提高效率,只有在合适的地方设置并行才能达到事半功倍的效果。