zoukankan      html  css  js  c++  java
  • OpenMP 中的线程任务调度

    OpenMP中任务调度主要针对并行的for循环,当循环中每次迭代的计算量不相等时,如果简单地给各个线程分配相同次数的迭代,则可能会造成各个线程计算负载的不平衡,影响程序的整体性能。

    如下面的代码中,如果每个线程执行的任务数量平均分配,有的线程会结束早,有的线程结束晚:

    复制代码
     1 #include<stdio.h>
     2 #include<omp.h>
     3 
     4 int main(){
     5     int a[100][100] = {0};
     6 #pragma omp parallel for
     7     for (int i =0; i < 100; i++){
     8         for(int j = i; j < 100; j++ )
     9             a[i][j] = ((i%7)*(j%13)%23);
    10     }
    11     return 0;
    12 }
    复制代码

    为此,OpenMP提供了schedule子句来实现任务的调度。

    schedule子句:

      schedule(type[, size]),

      参数type是指调度的类型,可以取值为static,dynamic,guided,runtime四种值。其中runtime允许在运行时确定调度类型,因此实际调度策略只有前面三种。

      参数size表示每次调度的迭代数量,必须是整数。该参数是可选的。当type的值是runtime时,不能够使用该参数。

    1.静态调度static

      大部分编译器在没有使用schedule子句的时候,默认是static调度。static在编译的时候就已经确定了,那些循环由哪些线程执行。

      当不使用size 时,将给每个线程分配┌N/t┐个迭代。当使用size时,将每次给线程分配size次迭代。

      如下面代码:

    复制代码
     1 #include<stdio.h>
     2 #include<omp.h>
     3 int main(){
     4     int a[100][100] = {0};
     5 #pragma omp parallel for schedule(static)
     6 //#pragma omp parallel for schedule(static,5)
     7     for (int i =0; i < 100; i++){
     8         printf("id=%d i=%d
    ",omp_get_thread_num(),i);
     9     }
    10     return 0;
    11 }
    复制代码
     在四核机器上执行:

      (1)当不使用参数时,100/4=25,0-24由1号线程执行;25-49由2号线程执行;50-74由3号线程执行;75-99由4号线程执行

      (1)当不使用参数时,x(x=0,1,2,3)线程执行((n/5)%4)任务。其中n=0-99。

    2.动态调度dynamic

      动态调度依赖于运行时的状态动态确定线程所执行的迭代,也就是线程执行完已经分配的任务后,会去领取还有的任务。由于线程启动和执行完的时间不确定,所以迭代被分配到哪个线程是无法事先知道的。

      当不使用size 时,是将迭代逐个地分配到各个线程。当使用size 时,逐个分配size个迭代给各个线程。

      如下面代码:

    复制代码
     1 #include<stdio.h>
     2 #include<omp.h>
     3 int main(){
     4     int a[100][100] = {0};
     5 #pragma omp parallel for schedule(dynamic)
     6 //#pragma omp parallel for schedule(dynamic,5)
     7     for (int i =0; i < 100; i++){
     8         printf("id=%d i=%d
    ",omp_get_thread_num(),i);
     9     }
    10     return 0;
    11 }
    复制代码

    3.启发式调度guided

       采用启发式调度方法进行调度,每次分配给线程迭代次数不同,开始比较大,以后逐渐减小。

      size表示每次分配的迭代次数的最小值,由于每次分配的迭代次数会逐渐减少,少到size时,将不再减少。如果不知道size的大小,那么默认size为1,即一直减少到1。具体采用哪一种启发式算法,需要参考具体的编译器和相关手册的信息。

    三种运行方式总结:

    静态调度static:每次哪些循环由那个线程执行时固定的,编译调试。由于每个线程的任务是固定的,但是可能有的循环任务执行快,有的慢,不能达到最优。

    动态调度dynamic:根据线程的执行快慢,已经完成任务的线程会自动请求新的任务或者任务块,每次领取的任务块是固定的。

    启发式调度guided:每个任务分配的任务是先大后小,指数下降。当有大量任务需要循环时,刚开始为线程分配大量任务,最后任务不多时,给每个线程少量任务,可以达到线程任务均衡。

    OpenMP中任务调度主要针对并行的for循环,当循环中每次迭代的计算量不相等时,如果简单地给各个线程分配相同次数的迭代,则可能会造成各个线程计算负载的不平衡,影响程序的整体性能。

    如下面的代码中,如果每个线程执行的任务数量平均分配,有的线程会结束早,有的线程结束晚:

    复制代码
     1 #include<stdio.h>
     2 #include<omp.h>
     3 
     4 int main(){
     5     int a[100][100] = {0};
     6 #pragma omp parallel for
     7     for (int i =0; i < 100; i++){
     8         for(int j = i; j < 100; j++ )
     9             a[i][j] = ((i%7)*(j%13)%23);
    10     }
    11     return 0;
    12 }
    复制代码

     

    为此,OpenMP提供了schedule子句来实现任务的调度。

    schedule子句:

      schedule(type[, size]),

      参数type是指调度的类型,可以取值为static,dynamic,guided,runtime四种值。其中runtime允许在运行时确定调度类型,因此实际调度策略只有前面三种。

      参数size表示每次调度的迭代数量,必须是整数。该参数是可选的。当type的值是runtime时,不能够使用该参数。

    1.静态调度static

      大部分编译器在没有使用schedule子句的时候,默认是static调度。static在编译的时候就已经确定了,那些循环由哪些线程执行。

      当不使用size 时,将给每个线程分配┌N/t┐个迭代。当使用size时,将每次给线程分配size次迭代。

      如下面代码:

    复制代码
     1 #include<stdio.h>
     2 #include<omp.h>
     3 int main(){
     4     int a[100][100] = {0};
     5 #pragma omp parallel for schedule(static)
     6 //#pragma omp parallel for schedule(static,5)
     7     for (int i =0; i < 100; i++){
     8         printf("id=%d i=%d
    ",omp_get_thread_num(),i);
     9     }
    10     return 0;
    11 }
    复制代码

      在四核机器上执行:

      (1)当不使用参数时,100/4=25,0-24由1号线程执行;25-49由2号线程执行;50-74由3号线程执行;75-99由4号线程执行

      (1)当不使用参数时,x(x=0,1,2,3)线程执行((n/5)%4)任务。其中n=0-99。

    2.动态调度dynamic

      动态调度依赖于运行时的状态动态确定线程所执行的迭代,也就是线程执行完已经分配的任务后,会去领取还有的任务。由于线程启动和执行完的时间不确定,所以迭代被分配到哪个线程是无法事先知道的。

      当不使用size 时,是将迭代逐个地分配到各个线程。当使用size 时,逐个分配size个迭代给各个线程。

      如下面代码:

    复制代码
     1 #include<stdio.h>
     2 #include<omp.h>
     3 int main(){
     4     int a[100][100] = {0};
     5 #pragma omp parallel for schedule(dynamic)
     6 //#pragma omp parallel for schedule(dynamic,5)
     7     for (int i =0; i < 100; i++){
     8         printf("id=%d i=%d
    ",omp_get_thread_num(),i);
     9     }
    10     return 0;
    11 }
    复制代码

    3.启发式调度guided

       采用启发式调度方法进行调度,每次分配给线程迭代次数不同,开始比较大,以后逐渐减小。

      size表示每次分配的迭代次数的最小值,由于每次分配的迭代次数会逐渐减少,少到size时,将不再减少。如果不知道size的大小,那么默认size为1,即一直减少到1。具体采用哪一种启发式算法,需要参考具体的编译器和相关手册的信息。

     

    三种运行方式总结:

    静态调度static:每次哪些循环由那个线程执行时固定的,编译调试。由于每个线程的任务是固定的,但是可能有的循环任务执行快,有的慢,不能达到最优。

    动态调度dynamic:根据线程的执行快慢,已经完成任务的线程会自动请求新的任务或者任务块,每次领取的任务块是固定的。

    启发式调度guided:每个任务分配的任务是先大后小,指数下降。当有大量任务需要循环时,刚开始为线程分配大量任务,最后任务不多时,给每个线程少量任务,可以达到线程任务均衡。

  • 相关阅读:
    Fiddler基础与HTTP状态码
    Fiddler与F12设置代理
    人和机器猜拳游戏
    ng-model 取不到值
    git的使用
    笔记
    INSPIRED启示录 读书笔记
    INSPIRED启示录 读书笔记
    INSPIRED启示录 读书笔记
    INSPIRED启示录 读书笔记
  • 原文地址:https://www.cnblogs.com/mfryf/p/12307667.html
Copyright © 2011-2022 走看看