OpenMP(五)线程的调度优化

线程的调度优化

1. 引言

    通过前边的介绍,知道了并行区域,默认情况下会自动生成与CPU个数相等的线程,然后并行执行并行区域中的代码。对于并行区域中的for循环有特殊的声明方式,这样不同的线程可以分别运行for循环变量的不同部分。通过锁同步(atomic、critical、mutex函数)或事件同步(nowait、single、section、master)来实现并行区域的同步控制。

 

    那么系统是如何对线程进行调度的呢?具体的调度策略均有底层完成,本节介绍几种for可以在上层对for循环进行控制的调度策略。

 

2. 调度策略

   

    调度策略                  功能                                                                                                 适用场合

    static                     循环变量区域分为n等份,每个线程平分n份任务                                       各个cpu的性能差别不大

 

    dynamic                 循环变量区域分为n等份,某个线程执行完1份之后执行其他需要执行的         cpu之间运行能力差异很大

                                  那一份任务

 

    guided                   循环变量区域由大到小分为不等的n份,运行方法类似dynamic                   由于任务比dynamic不同,

                                                                                                                                         所以可以减少调度开销

 

    runtime                  在运行时来适用上述三种调度策略中的一种,默认使用static

 

    示例:

3.1. static

 

   

view plain copy to clipboard print ?
·········10········20········30········40········50········60········70········80········90········100·······110·······120·······130·······140·······150
  1. #include <iostream>   
  2. #include <omp.h>   
  3.   
  4. int main()  
  5. {  
  6.   
  7. //static调度策略,for循环每两次迭代分为一个任务   
  8. #pragma omp parallel for schedule(static, 2)   
  9.     for(int i = 0; i < 10; ++i)  
  10.     {  
  11.     //被分为5个任务,其中循环0~1,4~5, 8~9分配给了第一个线程,   
  12.      //其余的分配给第二个线程   
  13.          std::cout << "thread id: " << omp_get_thread_num() << " value: " << i << std::endl;  
  14.     }  
  15.   
  16.     return 0;  
  17. }  
#include <iostream> #include <omp.h> int main() { //static调度策略,for循环每两次迭代分为一个任务 #pragma omp parallel for schedule(static, 2) for(int i = 0; i < 10; ++i) { //被分为5个任务,其中循环0~1,4~5, 8~9分配给了第一个线程, //其余的分配给第二个线程 std::cout << "thread id: " << omp_get_thread_num() << " value: " << i << std::endl; } return 0; }

 

 

3.2. dynamic

   

view plain copy to clipboard print ?
·········10········20········30········40········50········60········70········80········90········100·······110·······120·······130·······140·······150
  1. #include <iostream>   
  2. #include <omp.h>   
  3.   
  4. int main()  
  5. {  
  6.     //dynamic调度策略,for循环每两次迭代分为一个任务   
  7.     #pragma om parallel for schedule(dnamic, 2)   
  8.     for(int i = 0; i < 10; ++i)  
  9.     {  
  10.     //分为5个任务,只要有任务并且线程空闲,那么该线程会执行该任务   
  11.          std::cout << "thread id: " << omp_get_thread_num() << " value: " << i << std::endl;  
  12.     }  
  13.   
  14.     return 0;  
  15. }  
#include <iostream> #include <omp.h> int main() { //dynamic调度策略,for循环每两次迭代分为一个任务 #pragma om parallel for schedule(dnamic, 2) for(int i = 0; i < 10; ++i) { //分为5个任务,只要有任务并且线程空闲,那么该线程会执行该任务 std::cout << "thread id: " << omp_get_thread_num() << " value: " << i << std::endl; } return 0; }

 

 

3.3. guided

    guided调度策略与dynamic区别在于,所分的任务块是从大到小排列的。具体分块算法为:每块的任务大小为:【迭代次数/线程个数的二倍】。其中每个任务的最小迭代次数由guided声明设定,默认为1。

 

    举例说明:

   

view plain copy to clipboard print ?
·········10········20········30········40········50········60········70········80········90········100·······110·······120·······130·······140·······150
  1. #pragma omp for schedule [guided, 80]   
  2.   
  3. for(int i = 0; i < 800; ++i)  
  4. {  
  5.     // .....   
  6. }  
#pragma omp for schedule [guided, 80] for(int i = 0; i < 800; ++i) { // ..... }

 

 

两个cpu,那么任务分配如下:

第一个任务: [800/(2*2)] = 200

第二个任务:第一个任务分了200,还有600,那么[600/(2*2)] = 150

第三个任务:第二个任务分了150,还有450,那么[450/2*2)] = 113

第四个人任务:第三个任务分了113,还有337,那么[337/(2*2)] = 85

第五个任务:第四个任务分了85,还有252,那么[252/(2*2)] = 63, 小于声明的80,那么这里为80

第六个任务:第五个任务分了80,还有172,根据声明,这里为80(因为会小于80)

第七个任务:第六个任务分了80,还有92,根据声明,这里为80(因为会小于80)

第八个任务:第七个任务分了80,还有12,根据声明,这里为12(因为不够80)

 

3.4.runtime

运行时底层动态选择调度策略。

你可能感兴趣的:(OpenMP(五)线程的调度优化)