OpenMP中的任务调度

开发 前端
OpenMP中,任务调度主要用于并行的for循环中,当循环中每次迭代的计算量不相等时,如果简单地给各个线程分配相同次数的迭代的话,会造成各个线程计算负载不均衡,这会使得有些线程先执行完,有些后执行完,造成某些CPU核空闲,影响程序性能。

OpenMP中,任务调度主要用于并行的for循环中,当循环中每次迭代的计算量不相等时,如果简单地给各个线程分配相同次数的迭代的话,会造成各个线程计算负载不均衡,这会使得有些线程先执行完,有些后执行完,造成某些CPU核空闲,影响程序性能。例如以下代码:

  1. int i, j; 
  2. int a[100][100] = {0}; 
  3. for ( i =0; i < 100; i++) 
  4. for( j = i; j < 100; j++ ) 
  5.     a[i][j] = i*j; 

如果将最外层循环并行化的话,比如使用4个线程,如果给每个线程平均分配25次循环迭代计算的话,显然i=0和i=99的计算量相差了100倍,那么各个线程间可能出现较大的负载不平衡情况。为了解决这些问题,OpenMP中提供了几种对for循环并行化的任务调度方案。

在OpenMP中,对for循环并行化的任务调度使用schedule子句来实现,下面介绍schedule字句的用法。

1.1.1Schedule子句用法

schedule子句的使用格式为:

  1. schedule(type[,size]) 

schedule有两个参数:type和size,size参数是可选的。

1.  type参数

表示调度类型,有四种调度类型如下:

  • dynamic
  •  guided
  • runtime
  • static

这四种调度类型实际上只有static、dynamic、guided三种调度方式,runtime实际上是根据环境变量来选择前三种中的某中类型。

run-sched-var

2. size参数 (可选)

size参数表示循环迭代次数,size参数必须是整数。static、dynamic、guided三种调度方式都可以使用size参数,也可以不使用size参数。type参数类型为runtime时,size参数是非法的(不需要使用,如果使用的话编译器会报错)。

1.1.2静态调度(static)

当parallel for编译指导语句没有带schedule子句时,大部分系统中默认采用static调度方式,这种调度方式非常简单。假设有n次循环迭代,t个线程,那 么给每个线程静态分配大约n/t次迭代计算。这里为什么说大约分配n/t次呢?因为n/t不一定是整数,因此实际分配的迭代次数可能存在差1的情况,如果 指定了size参数的话,那么可能相差一个size。

静态调度时可以不使用size参数,也可以使用size参数。

3.不使用size参数

不使用size参数时,分配给每个线程的是n/t次连续的迭代,不使用size参数的用法如下:

schedule(static)

例如以下代码:

  1. #pragma omp parallel for schedule(static) 
  2.     for(i = 0; i < 10; i++ ) 
  3.     { 
  4.          printf("i=%d, thread_id=%d/n", i, omp_get_thread_num()); 

上面代码执行时打印的结果如下:

i=0, thread_id=0

i=1, thread_id=0

i=2, thread_id=0

i=3, thread_id=0

i=4, thread_id=0

i=5, thread_id=1

i=6, thread_id=1

i=7, thread_id=1

i=8, thread_id=1

i=9, thread_id=1

可以看出线程0得到了0~4次连续迭代,线程1得到5~9次连续迭代。注意由于多线程执行时序的随机性,每次执行时打印的结果顺序可能存在差别,后面的例子也一样。

4. 使用size参数

使用size参数时,分配给每个线程的size次连续的迭代计算,用法如下:

  1. schedule(static, size) 

例如以下代码:

  1. #pragma omp parallel for schedule(static, 2) 
  2.     for(i = 0; i < 10; i++ ) 
  3.     { 
  4.          printf("i=%d, thread_id=%d/n", i, omp_get_thread_num()); 

执行时会打印以下结果:

i=0, thread_id=0

i=1, thread_id=0

i=4, thread_id=0

i=5, thread_id=0

i=8, thread_id=0

i=9, thread_id=0

i=2, thread_id=1

i=3, thread_id=1

i=6, thread_id=1

i=7, thread_id=1

从打印结果可以看出,0、1次迭代分配给线程0,2、3次迭代分配给线程1,4、5次迭代分配给线程0,6、7次迭代分配给线程1,…。每个线程依次分配到2次连续的迭代计算。

#p#

1.1.3动态调度(dynamic)

动态调度是动态地将迭代分配到各个线程,动态调度可以使用size参数也可以不使用size参数,不使用size参数时是将迭代逐个地分配到各个线程,使用size参数时,每次分配给线程的迭代次数为指定的size次。

下面为使用动态调度不带size参数的例子:

  1. #pragma omp parallel for schedule(dynamic) 
  2.          for(i = 0; i < 10; i++ ) 
  3.          { 
  4.                   printf("i=%d, thread_id=%d/n", i, omp_get_thread_num()); 
  5.          } 

打印结果如下:

i=0, thread_id=0

i=1, thread_id=1

i=2, thread_id=0

i=3, thread_id=1

i=5, thread_id=1

i=6, thread_id=1

i=7, thread_id=1

i=8, thread_id=1

i=4, thread_id=0

i=9, thread_id=1

下面为动态调度使用size参数的例子:

  1. #pragma omp parallel for schedule(dynamic, 2) 
  2.          for(i = 0; i < 10; i++ ) 
  3.          { 
  4.                   printf("i=%d, thread_id=%d/n", i, omp_get_thread_num()); 
  5.          } 

打印结果如下:

i=0, thread_id=0

i=1, thread_id=0

i=4, thread_id=0

i=2, thread_id=1

i=5, thread_id=0

i=3, thread_id=1

i=6, thread_id=0

i=8, thread_id=1

i=7, thread_id=0

i=9, thread_id=1

从打印结果可以看出第0、1,4、5,6、7次迭代被分配给了线程0,第2、3,8、9次迭代则分配给了线程1,每次分配的迭代次数为2。

1.1.4guided调度(guided)

guided调度是一种采用指导性的启发式自调度方法。开始时每个线程会分配到较大的迭代块,之后分配到的迭代块会逐渐递减。迭代块的大小会按指数级下降到指定的size大小,如果没有指定size参数,那么迭代块大小最小会降到1。

例如以下代码:

  1. #pragma omp parallel for schedule(guided,2) 
  2.     for(i = 0; i < 10; i++ ) 
  3.     { 
  4.          printf("i=%d, thread_id=%d/n", i, omp_get_thread_num()); 

打印结果如下:

i=0, thread_id=0

i=1, thread_id=0

i=2, thread_id=0

i=3, thread_id=0

i=4, thread_id=0

i=8, thread_id=0

i=9, thread_id=0

i=5, thread_id=1

i=6, thread_id=1

i=7, thread_id=1

第0、1、2、3、4次迭代被分配给线程0,第5、6、7次迭代被分配给线程1,第8、9次迭代被分配给线程0,分配的迭代次数呈递减趋势,最后一次递减到2次。

1.1.5runtime调度(rumtime)

runtime调度并不是和前面三种调度方式似的真实调度方式,它是在运行时根据环境变量OMP_SCHEDULE来确定调度类型,最终使用的调度类型仍然是上述三种调度方式中的某种。

例如在unix系统中,可以使用setenv命令来设置OMP_SCHEDULE环境变量:

setenv OMP_SCHEDULE “dynamic, 2”

上述命令设置调度类型为动态调度,动态调度的迭代次数为2。

在windows环境中,可以在”系统属性|高级|环境变量”对话框中进行设置环境变量。

原文链接:http://blog.csdn.net/drzhouweiming/article/details/1844762

责任编辑:陈四芳 来源: blog.csdn.net
相关推荐

2013-12-16 17:17:01

OpenMp数据处理

2023-12-26 07:44:00

Spring定时调度

2023-05-08 16:38:46

任务调度分布式任务调度

2021-05-13 12:00:51

cron调度任务系统运维

2022-09-16 11:23:59

Python框架Celery

2021-05-20 09:50:20

鸿蒙HarmonyOS应用

2020-04-01 16:10:02

PythonAPScheduler调度

2021-06-28 06:00:11

systemd定时器系统运维

2023-11-16 09:30:27

系统任务

2022-12-20 10:22:16

计算函数

2023-12-13 13:03:53

任务调度执行XXLJOB

2023-10-06 12:15:02

2021-05-14 11:39:58

SchedulePython工具

2009-06-19 15:20:08

Quartz任务调度Spring

2022-09-21 12:01:22

消息队列任务队列任务调度

2020-09-29 19:20:05

鸿蒙

2023-06-26 00:14:28

Openjob分布式任务

2019-11-15 10:16:27

分布式任务框架

2012-11-01 11:29:33

IBMdw

2013-12-18 10:27:11

OpenMP线程
点赞
收藏

51CTO技术栈公众号