OpenMP: sections分段并行

       除了循环结构可以进行并行之外,还可以进行分段并行(parallel section)。迄今为止,每谈到如何去并行一个程序时,我们主要关心的是在同一时间将一个任务划分成多个然后用多线程去完成。然而,如果这个程序的一系列操作中后面的计算并不依赖于前面的计算,意思就是说它们之间相互并不依赖,这将有利于分配不同的任务给不同的线程去执行。当然,这对于那些执行过程非常短(总体计算量非常小)或程序本身具有很强的依赖性(前面与后面之间、循环之间),要实现并行是非常困难或不可能的。为了对付这种情况,OpenMP提供了分段协同工作结构(sections work-sharing construct),它可以使整个程序任务并行分割,并指定每个任务给不同的线程。在Fortran中程序中以sections指令开头,end sections指令结束,其中每段以section指令开始。
      C中sections指令结构如下:
#pragma omp sections [clause[[,] clause] .. ]
{
[#pragma omp section]
structured-block
[#pragma omp section]
structured-block
……………..
}
      其中条件clause可以为private(list)、firstprivate(list)、lastprivate(list)、reduction(operator: list)和 等。
      采用section定义的每段程序都将只执行一次,sections中的每段section将并行执行。一个程序中可以定义多个sections,每个sections中又可以定义多个section。同一个sections中section之间处于并行状态。sections与其他sections之间处于串行状态。

      如下在程序中定义两段sections,其中每段都有两个section,代码如下:

//File: SectionsTest.cpp
#include"stdafx.h"
#include<omp.h>
#include<iostream>
using namespace std;
void SectionsTest()
{
	int i;
	#pragma omp parallel num_threads(4)
	{
		#pragma omp sections //第1个sections
		{
			#pragma omp section
			{
				cout<<"section 1 线程ID:"<<omp_get_thread_num()<<"\n";
			}
			#pragma omp section
			{
				cout<<"section 2 线程ID:"<<omp_get_thread_num()<<"\n";
			}
		}
		#pragma omp sections //第2个sections
		{
			#pragma omp section
			{
				cout<<"section 3 线程ID:"<<omp_get_thread_num()<<"\n";
			}
			#pragma omp section
			{
				cout<<"section 4 线程ID:"<<omp_get_thread_num()<<"\n";
			}
		}
	}
}

      运行程序,其结果有:

section 1 线程ID:0
section 2 线程ID:1
section 3 线程ID:3
section 4 线程ID:1

      或者其他的结果,但是都会发现section 1与section 2的线程ID永远都可能相同(除非CPU只支持一个线程,或上面代码中num_threads设置为1),同样,section 3与section 4的线程ID也不可能相同,但是,section 1的线程ID有可能与section 3或section 4的线程ID相同,section 2的线程ID有可能与section 3或section 4的线程ID相同。之所以出现这种现象的原因就是,第一个sections与第二个sections在程序中处于串行,而第一个sections中的section1和section2它们处于并行,第二个sections中的section3和section4也处于并行。上面程序的运行示意图如下图所示:

      如果将上段程序中num_threads设置为2,则计算结果中线程ID只会是0和1,这样可以更加明确sections之间的运行顺序。

      上例中并行是基于每个sections的,如果只有一个sections,其并行定义如下例子的代码:

//File: SectionsTest02.cpp
#include"stdafx.h"
#include<omp.h>
#include<iostream>
using namespace std;
void SectionsTest02()
{
	#pragma omp parallel sections num_threads(4)
	{
		#pragma omp section
		{
			cout<<"section 1 线程ID:"<<omp_get_thread_num()<<"\n";
		}
		#pragma omp section
		{
			cout<<"section 2 线程ID:"<<omp_get_thread_num()<<"\n";
		}
		#pragma omp section
		{
			cout<<"section 3 线程ID:"<<omp_get_thread_num()<<"\n";
		}
		#pragma omp section
		{
			cout<<"section 4 线程ID:"<<omp_get_thread_num()<<"\n";
		}
	}
}

运行程序,其结果可能为如下:

section 1 线程ID:0

section 3 线程ID:2

section 2 线程ID:1

section 4 线程ID:3

      很明显section1与2、3、4不一定按顺序输出,因为在这里它们已经并行,输出顺序具体要看那个线程先执行完,不像前面的代码中第一个sections肯定是在第二个sections的前面输出。

      如果要使sections之间并行,只需要在sections后加上nowait指令即可,如下例子:

//File: SectionsTest03.cpp
#include"stdafx.h"
#include<omp.h>
#include<iostream>
using namespace std;
void SectionsTest03()
{
	#pragma omp parallel
	{
		#pragma omp sections nowait //第个sections
		{
			#pragma omp section
			{
				cout<<"section 1 线程ID:"<<omp_get_thread_num()<<"\n";
			}
			#pragma omp section
			{
				cout<<"section 2 线程ID:"<<omp_get_thread_num()<<"\n";
			}
			#pragma omp section
			{
				cout<<"section 3 线程ID:"<<omp_get_thread_num()<<"\n";
			}
		}
		#pragma omp sections //第个sections
		{
			#pragma omp section
			{
				cout<<"section 4 线程ID:"<<omp_get_thread_num()<<"\n";
			}
			#pragma omp section
			{
				cout<<"section 5 线程ID:"<<omp_get_thread_num()<<"\n";
			}
			#pragma omp section
			{
				cout<<"section 6 线程ID:"<<omp_get_thread_num()<<"\n";
			}
		}
	}
}

      运行程序,其结果如下:

section 1 线程ID:6

section 5 线程ID:3

section 2 线程ID:1

section 4 线程ID:4

section 3 线程ID:5

section 6 线程ID:2

阅读更多
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页