当前位置:   article > 正文

并行处理及分布式系统 第五章 用OpenMP进行共享内存编程_#pragma omp parallel for num_threads

#pragma omp parallel for num_threads

一、一些概念

        1、预处理指令:预处理指令以#pragma开头,通常把字符#顶格写,移动指令的剩余部分使

        它和剩下的代码对齐。与所有预处理器指令一样,pragma的默认长度是一行,如果有一个

        pragma在一行中放不下,新行需要在前面加一个反斜杠"\"进行转义。OpenMP的pragma以

        pragma omp开头

        2、OpenMP的头文件是omp.h

        3、结构化代码块:是一条C语句或只有一个入口和一个出口的一组符合C语句,在这个代码

        块中允许调用exit函数。

        4、线程组:在OpenMP语法中,执行并行块线程的集合(原始线程和新的线程)称为线程组

             主线程:原始的线程

             从线程:额外的线程

        5、当代码块执行完时,完成代码块的线程将等待线程组中的所有其他线程完成代码块,当所

        有线程都完成了代码块后,从线程将终止,主线程将继续执行之后的代码。

        6、在parallel指令前已经被声明的变量,拥有在线程组中所有线程间的共享作用域,而在块中

        声明的变量(如函数中的局部变量)有私有作用域。

        7、规约:将相同的规约操作符重复的应用到操作数序列来得到一个结果的计算。

              规约变量:所有规约操作的中间结果存储在同一个变量里,这个变量就是规约变量

              规约操作符:是一个二元操作符(如加法或减法)

        8、规约操作的一些说明

                ①OpenMP为每个线程有效地创建了一个私有变量,运行时系统在这个私有变量中存储

                每个线程的运算结果。

                ②OpenMP还创建了一个临界区,在进行规约操作时会将存储在私有变量中的值规约到

                这个临界区中,这个临界区可以简单的理解为规约变量

                ③由于浮点运算不满足结合律,如果有个规约变量是一个float或者double型数据,那么

                当使用不同数量的线程时,结果可能会有些许不同。

                ④当一个变量被包含子在一个reduction子句中时,变量本身是共享的。然而线程组中的

                每个线程都创建自己的私有变量,在parallel块里,每当一个线程执行涉及这个变量的语

                句时,它使用的其实是私有变量。当parallel块结束后,私有变量中的值被整合到一个共

                享变量中。

        9、使用parallel for的注意点:

                ①OpenMP只会并行化for循环,不会并行化while或do-while循环。

                ②OpenMP只能并行化确定迭代次数的for循环,例如:

                        由for语句本身(即for(...;...;...))确定了次数的for循环

                        无限循环不能被并行化

                        添加了break的循环循环次数是不定的,因为break添加了一个循环退出口,循环次

                        数就无法确定了

                        在循环体中可以有一个exit调用,这样的循环也可以并行化。

                ③OpenMP编译器不会检查parallel指令并行化的训环包含的迭代间的依赖关系,例如斐

                波那切数列

                ④一个或更多个迭代结果依赖于其他迭代的训环一般不能被OpenMP正确的并行化。

        10、OpenMP能够并行化的典型的for循环结构

        说明:

                ①变量index必须是整型或指针类型

                ②表达式start、end和incr必须有一个兼容的类型

                ③start、end和incr不能在循环执行期间改变

                ④在循环执行期间,变量index只能够被for语句中的“增量表达式”修改。

        11、数据依赖:同一个线程计算间的依赖关系称为数据依赖

                循环依赖:一个迭代中计算的值在之后的迭代中使用的依赖关系称为循环依赖

        12、在使用互斥技术时的注意点:

                ①对同一个临界区不应当混合使用不同的互斥机制

                ②互斥执行不保证公平性,也就是说可能某个线程会一直阻塞以等待对某个临界区的执

                行

                ③“嵌套”互斥结构可能会产生意料不到的结果。

二、openMP编程代码

        编译程序

$ gcc -g -Wall -fopenmp -o 程序名 程序名.c

        运行程序

  1. $ ./程序名 thread_num
  2. //thread_name是要运行的线程的个数

        获取通过命令行出入的线程数

  1. long strtol(const char* number_p /*in*/,char** end_p /*out*/,int base /*in*/);
  2. //number_p是一个字符串(可以是命令行参数)
  3. //base是字符串所表示的线程数,是一个数字
  4. //end_p不使用,只用传入一个NULL就可以

        parallel指令

  1. # pragma omp parallel
  2. //用来表明之后的结构化代码块(也称基本块)应该被多个线程并行执行
  3. # pragma omp parallel num_threads(thread_count)
  4. //在parallel指令中加入子句修饰,添加num_threads子句后就允许程序员指定执行后序代码块的线程数

        对每个线程进行编号

int omp_get_thread_num(void);

        获取线程组中的线程数

int omp_get_num_threads(void);

        错误检查——防止编译器不支持OpenMP,代码中却非法调用OpenMP函数导致错误

  1. #ifdef _OPENMP
  2. # include<omp.h>
  3. #endif
  4. #ifdef _OPENMP
  5. int my_rank=omp_get_thread_num();
  6. int thread_count=omp_get_num_threads();
  7. #else
  8. int my_rank=0;
  9. int thread_counnt=1;
  10. #endif
  11. //如果OpenMP无法使用,则后面代码将是单线程执行的,单线程的编号就是0,线程数将是1

        critical指令:告诉编译器需要安排线程对下列的代码块进行互斥访问

  1. # pragma omp critical
  2. //一次只允许一个线程能够执行下面的代码块

        规约子句

  1. # pragma omp parallel num_threads(thread_count) \
  2. reduction(<operator>:<variable list>)
  3. //在C语言中,预处理指令缺省情况下只有一行,如果有两行需要添加一个(\)来转义换行符
  4. //和num_threads一样reduction是一个子句,具体的用法和num_threads是一样的
  5. //在C 语言中,operator可能是操作符+、*、-、&、|、&&、||、^中的任意一个

        parallel for指令

  1. # pragma omp parallel for 子句
  2. //parallel for指令之后的结构化块必须是for循环
  3. // 在一个已经被parallel for指令并行化的for循环中,线程间的缺省划分方式被系统默认为是块划分。
  4. //在一个被parallel for指令并行化的循环中,循环变量的缺省作用域是私有的,比如在执行循环时控制循环次数的i是线程组中每个线程自己拥有副本的,是私有的。

        private子句

  1. # pragma omp parallel for num_threads(thread_count) \
  2. reduction(<operator>:<variable list>) \
  3. private(values)
  4. //在private子句内列举的变量在每一个线程上都有一个私有的副本被创建

         default子句

  1. # pragma omp parallel (for) default(none)
  2. //使用该子句编译器将要求我们明确在这个块中使用的每个变量和已经在块之外声明的变量的作用域

        for指令

  1. # pragma omp for
  2. //告诉OpenMP用已有的线程组来并行化for循环,并不自己创建任何线程

        schedule子句

  1. //用于在parallel for或者for指令中进行迭代分配
  2. # pragma omp parallel for num_threads(thread_count) \
  3. reduction(<operator>:<variable list>)\
  4. schedule(<type> [,<chunksize>])
  5. //type可以是如下类型:
  6. // static,迭代能够在循环执行前分配给线程
  7. // dynamic或guided。迭代在循环执行时被分配给线程,因此在一个线程完成了它的当前迭代集合后,它能 从运行时系统中请求更多
  8. // auto。编译器和运行时系统决定调度方式
  9. // runtime。调度在运行时决定
  10. //chunksize是一个整数。在OpenMP中,迭代块是在顺序循环中连续执行的一块迭代语句,块中的迭代次数是chunksize。只有static、dynamic和guided调度有chunksize
  11. //chunksize相当于决定了循环划分块的大小

        生产者消费者问题

  1. //线程进行消息传递的伪代码
  2. for(sent_msgs=0;send_msgs<send_max;sent_msgs++){
  3. Send_msg();
  4. Try_receive();
  5. }
  6. while(!Done())
  7. Try_receive();
  8. //消息发送函数Send_msg()函数的伪代码
  9. mesg=random();
  10. dest=random()%thread_count;
  11. # pragma omp critical
  12. Enqueue(queue,dest,my_rank,mesg);
  13. //接受消息函数Try_receive()函数的伪代码
  14. queue_size=enqueued-dequeued;
  15. if(queue_size==0) return;
  16. else if (queue_size==1)
  17. # pragma omp critical
  18. Dequeue(queue,&src,&mesg);
  19. else
  20. Dequeue(queue,&src,&mesg);
  21. Print_message(src,mesg);
  22. //终止检测函数Done()函数的伪代码
  23. queue_size=enqueued-dequeued;
  24. if(queue_size==0&&done_sending==thread_count)
  25. return TRUE;
  26. else
  27. return FALSE;
  28. //由于每个线程在执行完for循环后将不再发送任何消息,所以我们增加计数器done_sending,每个线程在for循环结束后将该计数器+1,以此来保证线程不会漏收消息

        显示路障指令

  1. # pragma omp barrier
  2. //当线程遇到路障时,它将被阻塞,直到组中所有的线程都到达了这个路障。当组中所有的线程都到达了这个路障时,这些线程就可以接着往下执行

        atomic指令

  1. # pragma omp atomic
  2. //该指令只能保护一条C语言赋值语句所形成的临界区,且该赋值语句必须是以下几种形式之一:
  3. // x <op> = <expression>;在expression中不能引用x
  4. // x++;
  5. // ++x;
  6. // x--;
  7. // --x;
  8. //<op>可以是任意二元操作符:+、*、-、/、&、^、|、<<、>>

        OpenMP的简单锁类型:omp_lock_t

  1. void omp_init_lock(omp_lock_t* lock_p /*out*/);
  2. void omp_set_lock(omp_lock_t* lock_p /*/in/out*/);
  3. void omp_unset_lock(omp_lock_t* lock_p /*in/out*/);
  4. void omp_destroy_lock(omp_lock_t* lock_p /*in/out*/);

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/菜鸟追梦旅行/article/detail/275946
推荐阅读
相关标签
  

闽ICP备14008679号