1. for指令用途

for指令指定紧随其后的循环迭代由一组线程并行执行。

2. for指令语法格式

当for指令在并行区域内部时,其语法为

#pragma omp for [clause ...]  newline 
                schedule (type [,chunk]) 
                ordered
                private (list) 
                firstprivate (list) 
                lastprivate (list) 
                shared (list) 
                reduction (operator: list) 
                nowait 

   for_loop

也可将for指令与并行区域指令合并为一条,其语法为

#pragma omp parallel for ......
2.1 schedule子句

schedule子句描述如何将循环迭代划分给一组线程。包含以下几种方式(type字段)

  • 静态(static)
    循环迭代被分成多个块(每块大小由chunk变量指定,最后一个块可能没有chunk大小),然后线程的顺序静态地分配给各线程。如果未指定块大小,则循环迭代将在线程之间均匀地(如果可能的话)连续地划分。
    下面的代码示例
	int i;
	int chunk = 3;
#pragma omp parallel shared(chunk) private(i)
	{
	#pragma omp for schedule(static, chunk) nowait
		for (i=0; i < 20; i++)
		{
			printf("thread %d excute i = %d\n", omp_get_thread_num(), i);
		} 
	}

运行结果如下,再次运行迭代与线程的分配关系都保持不变,这就是静态的含义。可以看出,迭代是以块大小(3)按照顺序依次分配给各线程的。

  • 动态(dynamic)
    循环迭代被划分为多个块(每块大小由chunk变量指定,最后一个块可能没有chunk大小),并在线程之间动态调度;当一个线程执行完一个块时,再请求另一个块。默认块大小为1。
    下面的代码示例
	int i;
	int chunk = 3;
#pragma omp parallel shared(chunk) private(i)
	{
	#pragma omp for schedule(dynamic, chunk) nowait
		for (i=0; i < 20; i++)
		{
			printf("thread %d excute i = %d\n", omp_get_thread_num(), i);
		} 
	}

运行结果如下,再次运行迭代与线程的分配关系会变化,但迭代是以块大小来分配的,只不过不是按照线程id顺序,而是根据线程运行情况动态分配块。

  • 引导的(guided)
    开始时循环迭代划分成块的大小与未分配迭代次数除以线程数成比例,然后随着循环迭代的分配,块大小会减小为chunk值。chunk的默认值为1。
    下面的代码示例
	int i;
	int chunk = 3;
#pragma omp parallel shared(chunk) private(i)
	{
	#pragma omp for schedule(guided, chunk) nowait
		for (i=0; i < 20; i++)
		{
			printf("thread %d excute i = %d\n", omp_get_thread_num(), i);
		} 
	}

运行结果如下,可以看出分配的各线程的连续迭代次数(即块大小)是依次减小的,但不会小于chunk值(3)。

  • 运行时(runtime)
    将调度决策推迟到运行时来决定,这时不能指定块大小。
2.2 nowait子句

如果设置了nowait子句,则在并行循环的结束处不会进行线程同步,先执行完循环的线程会继续执行后续代码。
下面示例代码

#pragma omp parallel
	{
	#pragma omp for nowait
		for (int i=0; i < 5; i++)
		{
			printf("loop1 thread %d excute i = %d\n", omp_get_thread_num(), i);
		}

	#pragma omp for 
		for (int j=0; j < 5; j++)
		{
			printf("loop2 thread %d excute j = %d\n", omp_get_thread_num(), j);
		} 
 
	}

左边是不设置nowait的运行结果,所有线程都完成循环1,才会运行循环2,所以循环2的打印都在循环1之后。
右边是设置了nowait的运行结果,线程不用等待所有线程完成循环1再去执行循环2,所以循环1和循环2的打印是交错的

更多推荐

OpenMP编程(3)—for指令(含schedule、nowait)