其他分享
首页 > 其他分享> > CPU作业之openMp实现快速排序

CPU作业之openMp实现快速排序

作者:互联网

博客原文
欢迎关注博客:GaoMing’s blog

项目介绍

openMP相关知识

什么是openMP

OpenMP是由OpenMP Architecture Review Board牵头提出的,并已被广泛接受的、用于共享内存并行系统的多线程程序设计的一套编译指令 (Compiler Directive)

OpenMP支持、CC++Fortran;而支持OpenMP的编译器包括Sun CompilerGNU CompilerIntel Compiler等。

OpenMP提供了对并行算法的高层的抽象描述,程序员通过在源代码中加入专用的pragma来指明自己的意图,由此编译器可以自动将程序进行并行化,并在必要之处加入同步互斥以及通信。

当选择忽略这些pragma,或者编译器不支持OpenMP时,程序又可退化为通常的程序(一般为串行),代码仍然可以正常运作,只是不能利用多线程来加速程序执行。

openMP的Fork-Join并行执行模型

openMP版本

openMP编译制导

所有的编译制导指令必须以#pragma omp开头

  1. parallel指令

    • 用于构造一个并行块,如果它所构造的并行块有多于1条的语句,需要用一对大括号括起来。
    • 并行区域必须是一个完整的代码块,不能使用goto或其他语句跳出或转入。
    #include <stdio.h>
    int main(){
        #pragma omp parallel
        printf("The parallel region is run by thread %d.\n",omp_get_thread_num());
        return 0;
    }
    
  2. for指令

    • n用于对循环工作进行并行化,它通常需要与parallel合并使用。

    • for循环必须具备一定的规范格式:

      //没有与parallel同时使用,不能并行
      #include <stdio.h>
      int main(){
          int i;
          #pragma omp for
          for (i = 0; i < 4; i++)
              printf("i = %d, threadId = %d.\n", i, omp_get_thread_num());
          return 0;
      }
      
      
      //与parallel同时使用,能并行
      #include <stdio.h>
      int main(){
          int i;
          #pragma omp parallel for
          for (i = 0; i < 4; i++)
              printf("i = %d, threadId = %d.\n", i, omp_get_thread_num());
          return 0;
      }
      
  3. sections和section指令

    • 用于使各个线程执行不同的工作。

    • 每个section必须是一个结构化的代码块,不能有分支转入或跳出。

    • sections中可以定义多个section,每个section仅被一个一线程执行一次。

    • 当线程多于section数量时,每个线程最多执行一个section

    • 当线程少于section数量时,有线程会执行多于一个的section

      格式:

      #pragma omp sections [clause[[,] clause]…]
      {
           [#pragma omp section]
               structured  block
           [#pragma omp section]
               structured block
            ……
      }
      
      #include <stdio.h>
      int main(){
          #pragma omp parallel sections
          {
              #pragma omp section
              printf("Hello from %d.\n", omp_get_thread_num());
              #pragma omp section
              printf("Hi from %d.\n", omp_get_thread_num());
              #pragma omp section
              printf("Bye from %d.\n", omp_get_thread_num());
          }
          return 0;
      }
      
      
      输出:
      Bye from 17.
      Hi from 28.
      Hello from 36.
      
  4. single指令

    • 用于让紧随其后的语句串行执行

      例子:

      #include <stdio.h>
      #include <omp.h>
      int main(){
          #pragma omp parallel    {
              printf("Run in parallel, thread id = %d.\n", omp_get_thread_num());
              #pragma omp single        {
                  printf("Run in sequence, thread id = %d.\n", omp_get_thread_num());
              }
              printf("Run in parallel, thread id = %d.\n", omp_get_thread_num());
          }
          return 0;
      }
      

      输出:

      Run in parallel, thread id = 11.
      Run in sequence, thread id = 11.
      Run in parallel, thread id = 19.
      Run in parallel, thread id = 30.
      
  5. private数据属性

    • 将一个或多个变量声明为线程的私有变量。

    • 每个线程都有它自己的变量私有副本,其他线程无法访问。

    • 即使在并行区域外有同名的共享变量,共享变量在并行区域内不起任何作用,并且并行区域内不会操作到外面的共享变量。

    • 并行区域内的private变量和并行区域外同名的变量没有存储关联。

    • 如果需要继承原有共享变量的值,则应使用firstprivate子句。

    • 如果需要在退出并行区域时将私有变量最后的值赋值给对应的共享变量,则可使用lastprivate子句。

      例子1:

      #include <stdio.h>
      int main(){
          int k, i;
          k = 100;
          #pragma omp parallel for firstprivate(k),lastprivate(k)
          for (i = 0; i < 8; i++)
          {
              k += i;
              printf("k = %d in thread %d.\n", k, omp_get_thread_num());
          }
          printf("Finally, k = %d.\n", k);
          return 0;
      }
      

      输出:

      输出:
      k = 101 in thread 1.
      k = 107 in thread 7.
      k = 105 in thread 5.
      k = 100 in thread 0.
      k = 103 in thread 3.
      k = 104 in thread 4.
      k = 102 in thread 2.
      k = 106 in thread 6.
      Finally, k = 107.
      

      例子2:

      //默认是shared
      #include <stdio.h>
      int main(){
         int i, j;
         #pragma omp parallel for
         for (i = 0; i < 2; i++){
           for (j = 0; j < 5; j++) //j共享,线程1只执行了1次
               printf("i=%d, j=%d from id=%d.\n", i, j, omp_get_thread_num());
         }
         return 0;
      }
      

      输出:

      i=0, j=0 from id=0.
      i=0, j=1 from id=0.
      i=0, j=2 from id=0.
      i=0, j=3 from id=0.
      i=0, j=4 from id=0.
      i=1, j=0 from id=1.
      
  6. shared数据属性

    例子:

    #include <iostream>  
    #include <omp.h>   
    using namespace std;  
    int main() {  
        int sum = 0;    
        cout << "Before: " << sum << endl;    
    #pragma omp parallel for shared(sum)   
        for (int i = 0; i < 10; ++i) {  
            sum += i;  //存在数据竞争
    }  
        cout << "After: " << sum << endl;  
        return 0;  
    } 
    
  7. 设置线程数量

    • 编译制导的num_threads()子句。如:#pragma omp parallel num_threads(8)
    • 在并行区域外,使用运行库例程omp_set_num_threads()设定并行区域中使用。如:omp_set_num_threads(10)
    • 环境变量OMP_NUM_THREADS。如:export OMP_NUM_THREADS = 3
  8. 运行库例程与环境变量

    • int omp_get_num_threads(void): 返回当前并行区域中的线程数量
    • int omp_get_thread_num(void): 返回值当前并行区域中,当前线程在线程组中的编号。这个编号从0开始
    • int omp_get_num_procs(void): 返回值为当前程序可以使用的CPU核数
    • double omp_get_wtime(void): **返回值是以秒为单位的墙上时间。**在并行区域开始前和结束后分别调用该函数,并求取两次返回值的差,便可计算出并行执行的时间。

openMP快排

设计思路

利用OpenMp设计多线程进行快速排序,递归实现多线程同时进行子块的快速排序,需要线程块log2(n)个

自定义的库(quickSort.h)

分析

性能分析

标签:num,thread,int,parallel,omp,printf,openMp,排序,CPU
来源: https://blog.csdn.net/qq_45769678/article/details/118711856