用动态任务调度器实现Parallel_For

系统 1708 0

注:本文主要内容摘自笔者所著的《多核计算与程序设计》一书,略有修改,后续还会继续发布系列文章,如有需要,可以考虑将一下地址加入到您的浏览器收藏夹中: http://software.intel.com/zh-cn/blogs/category/multicore/

从前面的 CNestTaskScheduler 的使用方法中可以发现,采用嵌套任务调度,可以很方便地将一个大区间拆分成更多的小区间,将各个拆分后的区间放入分布式队列中,然后各个线程再从分布式队列中取出相应的区间进行处理。

对于一个 for 循环来说,通常处理的都是一个区间,因此也可以使用任务调度的方式将其拆分成更小的区间进行并行化执行。下面就利用嵌套任务调度的方法来实现一个 Parall_For 功能。

1.         区间的描述: CRange

要实现对区间的分拆功能,使用一个类 CRange 来描述区间。在实际情况中,区间通常可以由两个整数表示区间开始和结束位置,也可以由两个迭代器变量来表示区间开始和结束位置。不过 CRange 是一个抽象接口类,它并不定义区间的开始和结束位置,区间的开始和结束位置由继承它的类去定义。 CRange 类用 C++ 定义如下:

class CRange {

protected:

    CNestTaskScheduler   * m_pTaskScheduler ;

public:

    CRange (){};

    CRange ( CNestTaskScheduler * p );

    void SetTaskScheduler ( CNestTaskScheduler * p );

    CNestTaskScheduler * GetTaskScheduler ();

 

    virtual CRange * Split () = 0;

};

CRange 类中,最重要的一个接口是 Split() 接口,这个接口负责将一个区间拆分成两个区间,一个区间继续存放在原来的 CRange 对象中,另外一个区间存放在返回的 CRange 对象中。如果返回值为 NULL ,表明原来的区间不需要进行分拆。

CRange 类本身是一个接口类,用它主要是作为 Parallel_For() 函数的接口函数, Parallel_For() 函数的原型如下:

void Parallel_For(CRange *pRange );

由于 Split() 是一个纯虚函数,因此传给 Parallel_For() 函数的参数必须是一个继续了 CRange 类的派生类的实例。

2.         CRange 对象的处理过程

对于每个 CRange 对象, Parallel_For() 中需要对它进行处理,处理是通过任务调度器中的任务入口函数来处理的,处理过程如下图所示:

 

4   CRange 对象的处理过程

上面的处理过程可以用 C++ 代码实现如下:

/**    CRange 的任务处理入口函数

 

         @param   void *pArg - 实际为一个 CRange 指针

         @return   unsigned int WINAPI - CAPI_FAILED 表示失败, CAPI_SUCCESS 表示成功   

*/

unsigned int WINAPI RangeProcessTask(void *pArg)

{

    CRange * pRange = ( CRange *) pArg ;

 

    if ( pRange == NULL )

    {

        return CAPI_FAILED ;

    }

 

    CRange * pNewRange = pRange -> Split ();

    if ( pNewRange == NULL )

    {

        delete pRange ;

         return CAPI_SUCCESS ;

    }

 

    CNestTaskScheduler * pTaskSched = pRange -> GetTaskScheduler ();

 

    pNewRange -> SetTaskScheduler ( pTaskSched );

 

    TASK t1 , t2 ;

    t1 . pArg = ( void *) pRange ;

    t1 . func = RangeProcessTask ;

    t2 . pArg = ( void *) pNewRange ;

     t2 . func = RangeProcessTask ;

 

    pTaskSched -> SpawnLocalTask ( t1 );

    pTaskSched -> SpawnTask ( t2 );

 

    return CAPI_SUCCESS ;

}

 

3.   Parall_For 的处理流程

有了上面的 RangeProcessTask() 函数后,就可以用它作为嵌套任务调度器的任务入口函数,以实现 Parallel_For 功能。

Parallel_For() 中,主要实现对 CRange 对象的任务调度处理,需要将参数 pRange 作为根任务传入到任务调度器中进行处理。

Parallel_For() 的处理过程如下:

 

5   Parallel_For 的处理过程

Parallel_For() 的代码如下:

/**    并行 for 循的处理函数

    将一个 CRange 进行并行处理

 

         @param   CRange *pRange - CRange 指针     

         @return   void -  

*/

void Parallel_For(CRange *pRange )

{

    CNestTaskScheduler   * p = new CNestTaskScheduler ;

    TASK     task ;

 

    task . func = RangeProcessTask ;

    task . pArg = pRange ;

 

    pRange -> SetTaskScheduler ( p );

 

    p -> BeginRootThread ( task );

 

    delete p ;

}

用动态任务调度器实现Parallel_For


更多文章、技术交流、商务合作、联系博主

微信扫码或搜索:z360901061

微信扫一扫加我为好友

QQ号联系: 360901061

您的支持是博主写作最大的动力,如果您喜欢我的文章,感觉我的文章对您有帮助,请用微信扫描下面二维码支持博主2元、5元、10元、20元等您想捐的金额吧,狠狠点击下面给点支持吧,站长非常感激您!手机微信长按不能支付解决办法:请将微信支付二维码保存到相册,切换到微信,然后点击微信右上角扫一扫功能,选择支付二维码完成支付。

【本文对您有帮助就好】

您的支持是博主写作最大的动力,如果您喜欢我的文章,感觉我的文章对您有帮助,请用微信扫描上面二维码支持博主2元、5元、10元、自定义金额等您想捐的金额吧,站长会非常 感谢您的哦!!!

发表我的评论
最新评论 总共0条评论