炼数成金 门户 CUDA 查看内容

OpenMP指令之共享工作构造(Work-Sharing)

2015-9-11 13:16| 发布者: 炼数成金_小数| 查看: 1540| 评论: 0|原作者: gengshenghong|来自: CSDN

摘要: 共享工作构造:Work-Sharing Constructs(1)Work-Sharing Constructs的特点和类型:共享工作构造的基本特点:1. 共享工作构造将它作用的代码段拆分到进入此区域的线程team的成员执行。2. 共享工作构造不产生新的线 ...

安全 C++ GUI

共享工作构造:Work-Sharing Constructs
(1)Work-Sharing Constructs的特点和类型:
共享工作构造的基本特点:
1. 共享工作构造将它作用的代码段拆分到进入此区域的线程team的成员执行。
2. 共享工作构造不产生新的线程。
3. 进入共享工作区域不会有等待(barrier),退出共享工作构造的时候会有等待。  说明:barrier中文含义为“障碍”“关卡”,我个人觉得理解为“等待”更好,即同步的概念。

共享工作构造的类型:
1. do-for结构:
在一个team内,共享一个循环的迭代,代表“数据并行”的类型。
2. sections结构:
将任务分离为不同的sections,每一个section由一个线程执行。可用于实现“函数并行”的类型。
3. single结构:
将一段代码串行化。
共享并行结构的限制:
为了使得指令并行执行,共享工作结构必须被一个并行区域包围。
一个线程组到达一个工作共享结构时,要么全部全线程被占用,要么不占用。
线程组的所有成员必须以相同的顺序到达连续的工作共享结构。
(此处翻译参考自:http://blog.csdn.net/saga1979/article/details/6265564,我目前还不太理解这几句话的真实含义)

(2)do-for指令
作用:指定之后的循环迭代由team内的线程并行执行,前提是已经使用parallel并行构造初始化(即被parallel块包围),否则在单处理器上串行执行。(那么多处理器呢?)下面是使用do-for指令的一个例子,并行执行for循环并打印for循环的条件变量,根据结果可以发现,在多核处理器上如果没有被parallel包围也是串行执行。
#define N   100 // 尽量让N大一点,否则无法说明问题  
  
int main(int argc, char *argv[])  
{  
    printf("Masterthread started\n\n");  
    printf("Using omp for\n");  
#pragma omp for  
    for(int i = 0;i < N;i++)  
    {  
        printf("%d\n",i);  
    }// End of for region  
    printf("\n");  
  
    printf("Using omp parallel for\n");  
#pragma omp parallel  
    {  
#pragma omp for  
        for(int i = 0;i < N;i++)  
        {  
            printf("%d\n",i);  
        }// End of for region  
    }// End of parallel region  
  
    printf("Masterthread finished\n");  
  
    return(0);  
}  
下面介绍do-for指令的格式(说明的是,其实对于C++,称为for指令即可,之所以称为do-for指令是由于Fortran中是do关键字):
#pragma omp for [clause ...]  newline   
                schedule (type [,chunk])   
                ordered  
                private (list)   
                firstprivate (list)   
                lastprivate (list)   
                shared (list)   
                reduction (operator: list)   
                collapse (n)   
                nowait  
for_loop  
首先需要知道的是,for指令后必须紧接一个for循环,否则编译就会出错。

子句:
schedule:描述循环迭代是如何在线程之间划分的。默认的调度是依赖编译器的实现。schedule子句只用于for指令。
shedule可以取值为:STATIC、DYNAMIC、GUIDED、RUNTIME、AUTO。其区别在其它博文讨论。
nowait:如果指定了nowait,那么线程在并行循环结束处不进行同步。notwait不仅仅用于for指令(只要是共享工作构造都可以使用)。
ordered:指定该循环迭代必须以串行方式执行。
collapse:指定在一个嵌套循环中,多少次循环被折叠到一个大的迭代空间并根据schedule子句来划分。所有相关循环中的迭代的执行顺序决定迭代空间中折叠迭代的顺序。关于详细理解和schedule一起在其它博文讨论。
其它的一些子句,都是和数据相关的子句,后面会专门讨论。

for指令的主要限制:
循环控制变量必须是一个整数,而且循环控制参数对于所有线程必须是一样的。
程序的正确性不能依赖于哪个线程运行在某个具体的迭代上。
for循环中不能有分支(如goto)跳出循环。

(3)sections/section指令
作用:sections指令包含了多个section代码块,sections指令不是一个迭代的工作共享区域,而是每个section被一个线程执行一次,不同的section由不同的线程执行。一个线程也有可能执行多个section。
sections/section指令格式:
#pragma omp sections [clause ...]  newline   
                     private (list)   
                     firstprivate (list)   
                     lastprivate (list)   
                     reduction (operator: list)   
                     nowait  
{  
#pragma omp section   newline   
    structured_block  
#pragma omp section   newline   
    structured_block  
}  
sections块结束有一个隐含的等待,除非指定了nowait子句。其它的子句都是和数据相关,后面讨论。
问题:如果线程数和section数目不同?如果线程数大于section数的时候,那么有些线程执行一个section,有些线程不执行section;如果线程数小于sectioin数,那么可能某些线程要执行多个section,具体依赖于编译器的实现。包括哪个线程执行哪个section,也是依赖于编译器实现。

sections指令限制:
不能跳出(goto)一个section块;section必须出现在sections块内,没有孤立的sections块。
补充:sections指令和for指令应该一样,必须在parallel指令内,才会并行化,否则串行执行。下面是使用sections的一个简单例子:
int main(int argc, char *argv[])  
{  
    printf("Masterthread started\n\n");  
  
    #pragma omp parallel  
    {  
#pragma omp sections  
        {  
#pragma omp section  
            {  
                printf("section 1\n");  
            }  
#pragma omp section  
            {  
                printf("section 2\n");  
            }  
#pragma omp section  
            {  
                printf("section 3\n");  
            }  
#pragma omp section  
            {  
                printf("section 4\n");  
            }  
        }// End of sections region  
    }// End of parallel region  
  
    printf("Masterthread finished\n");  
  
    return(0);  
}  
(可以去掉parallel查看结果,会发现是串行执行的,只有使用parallel才会并行。)

(4)single指令
single指令作用:指定代码块由team内的一个线程执行,即相当于串行执行。主要可能用于使用sections的非线程安全的代码,比如IO处理等。
single指令格式:
#pragma omp single [clause ...]  newline   
                   private (list)   
                   firstprivate (list)   
                   nowait  
    structured_block  
默认情况下,不执行single指令的线程会在single块结尾处等待,除非使用了nowait。说明的是,single只是让team内的一个线程去执行这一段代码段,并不表示程序是单线程的,所以,如果不使用nowait,team内的其他线程都会等待着一个线程执行完毕。
下面的例子能帮助理解single的效果:
int main(int argc, char *argv[])  
{  
    printf("Masterthread started\n\n");  
  
#pragma omp parallel  
    {  
#pragma omp single  
        {  
            printf("single block\n");  
        }// End of single region    // other threads will wait here except using nowait  
        printf("parallel block\n");  
    }// End of parallel region  
  
    printf("Masterthread finished\n");  
  
    return(0);  
}  

结果:
另外需要说明的是,single指令允许跳进或跳出(goto)代码块。

鲜花

握手

雷人

路过

鸡蛋

最新评论

热门频道

  • 大数据
  • 商业智能
  • 量化投资
  • 科学探索
  • 创业

即将开课

热门文章

     

    GMT+8, 2020-1-21 19:47 , Processed in 0.135386 second(s), 23 queries .