读书笔记

浅谈Opem_MP

2017-07-02  本文已影响0人  一梦换须臾_

OpenMP2.5

有底层API后,就已经可以实现并行编程;然而,很多时候串行算法已经成型,如果继续使用原有的底层API,还将面临转换和调试的问题。OpenMP正是为了解决这样的问题。

一、OpenMP的介绍

1.概览

2.本质

3.实现层次

4.历史

(略)

5.OpenMP的目标

标准化

简洁有效

易用性

可移植性

6.OpenMP编程模型

共享内存、基于线程的并行模型

显式并行

Fork-Join模型

基于编译器指导语句

支持嵌套并行

动态线程的创建与销毁

线程的数量可以由OpenMP自适应

I/O

内存模型

7.OpenMP的层次

8.示例代码

    #include <omp.h>
    
    void main()
    {
        #pragma omp parallel            //编译指导语句,将大括号括起的范围内做成一个并行区
        {
            int ID=omp_get_thread_num();
            printf("hello(%d)",ID);
            printf("world(%d)\n",ID);
        }
    }

编译时,需要增加参数-fopenmp(gcc)、-mp(pgi)、/Qopenmp(Intel)、/openmp(Visual Studio,或直接在项目属性中添加OpenMP支持)

更一般的形式

    #include <omp.h>
    int main()
    {
        int v1,v2,v3;
        //Serial code
        #pragma omp parallel private(v1,v2) shared(v3)
        {
            //
            //Join
        }
        //Back to serial code
    }

二、创建线程

1.Fork-Join结构

2.指定线程的个数

虽然线程个数可以由OpenMP自动指定,但是也可以手动设置

omp_set_num_threads(4);

这使得此函数之后的每个并行区都是4个线程同时运行

也可以使用指导语句,这样只对一个并行区生效

`#pragma omp parallel num_threads(4)`

三、同步方式

1.临界区

多线程同时只能由一个进入临界区执行

    float res;
    #pragma omp parallel
    {
        float B;
        int i,id,nthrds;
        id=omp_get_thread_num();        //当前线程的ID
        nthrds=omp_get_num_threads();   //当前的线程个数
        for(i=id,i<niters;i+=thrds)     //巧妙的for循环,尽可能将循环任务平均地分配到各线程中去
        {
            B=big_job(i);
            #pragma omp critical
                consume(B,res);
        }
    }

2.原子操作

原子操作不会被多线程打断
然而原子操作和临界区的功能是一样的,因为有复合语句的存在,原子操作的功能实际上还要弱一些
原子操作中不能使用复合语句,也不能进行函数调用

    #pragma omp parallel
    {
        double tmp,B;
        B=DOIT();
        tmp=big_ugly(B);
        #pragma omp atomic
            X+=tmp;
    }

3.路障同步

4.同步次序

5.flush

6.锁

四、并行循环

1.SPMD与worksharing

2.分配循环用的worksharing

    #pragma omp for
        for(i=0;i<N;i++)
        {
            something();
        }

3.worksharing的结构特点

4.worksharing结构的限制

5.worksharing结构的类型

6.parallel与worksharing的组合

    double res[MAX];
    int i;
    #pragma omp parallel for
        for(i=0;i<MAX;i++)
            res[i]=huge();

7.规约

编译指导语句的基本格式

`#pragma omp directive-name [clause,...] newline`

规约指导语句

`reduction(op:list)`

归约操作的操作符和初始值

五、同步

1.Barrier

    #pragma omp barrier             //手动的路障同步
    #pragma omp for nowait          //指明取消末尾的隐式路障同步

2.Master结构

3.Single结构

4.ordered

5.锁

简单锁

可以认为是简单的布尔变量
omp_*_lock

嵌套锁

与简单锁不同,可以被同一个进程反复地加锁,解锁时也要进行相应数量的解锁
omp_*_nest_lock

简单锁的例子

    #include <omp.h>
    omp_lock_t lock;
    omp_init_lock(&lck);
    
    #pragma omp parallel private(tmp,id)
    {
        id=omp_get_thread_num();
        tmp=do_lots_of_work(id);
        omp_set_lock(&lock);
        omp_unset_lock(&lock);
    }
    omp_destroy_lock(&lock);

六、OpenMP的库函数

1.修改、设置线程数量

2.是否在并行区域内

3.是否允许系统动态调整线程数量

4.系统处理器数量

5.环境变量

环境变量的优先级比库函数要低一些

七、数据环境

1.默认存储属性

默认情况下的私有变量

2.private子句

3.firstprivate与lastprivate子句

4.default子句

default(PRIVATE|SHARED|NONE)

5.threadprivate子句

    int counter=0;
    #pragma omp threadprivate(counter)

copyin子句

    int a=100;
    #pragma omp threadprivate copyin(a)

copyprivate子句

指针的传递

        #pragma omp parallel private(x) shared(p0,p1)
        x=...;
        p0=&x;
在另一个线程中使用p0指针会造成不可预料的后果

八、Schedule子句

1.section子句

    #pragma omp parallel
    {
        #pragma omp sections
        {
            #pragma omp section
            calculation1();
            #pragma omp section
            calculation2();
            #pragma omp section
            calculation3();
        }
    }

2.schedule子句

`schedule(mode[,chunk])`

实际上大多数编译器除了static,另外三种都没实现

静态调度

动态调度

guided调度

runtime调度

九、内存模型

1.弱一致性

2.flush

    a=...;
    <other computaion>
    #pragma omp flush(a)

隐式数据同步

其它所有同步都会自动带上数据同步

十、OpenMP 3.0与任务

1.任务

2.例子

    for(int i=0;i<N;i+=a[i])
        task(a[i]);

3.task的结构

`#pragma omp task [clause[[,],clause]...]`

并行的链表举例

    #pragma omp parallel
    {
        #pragma omp single private(p)   //由一个线程进行预处理,其它线程什么都不做
        {
            p=listhead;
            while(p)
            {
                #pragma omp task
                process(p);             //将链表内多个结点的处理并行进行,
                                        //占用并行区内原本闲置的线程
                p=next(p);
            }
        }
    }

4.untied子句

举例

    #pragma omp single
    {
        #pragma omp task untied
        for(i=0;i<ONEZILLION;i++)
            #pragma omp task
            process(item[i]);
    }

5.if子句

上一篇 下一篇

猜你喜欢

热点阅读