zoukankan      html  css  js  c++  java
  • AMD OpenCL大学课程(10)

    GPU线程及调度

         本节主要讲述OpenCL中的Workgroup如何在硬件设备中被调度执行。同时也会讲一下同一个Workgroup中的workitem,如果它们执行的指令发生diverage(就是执行指令不一致)对性能的影响。学习OpenCL并行编程,不仅仅是对OpenCL Spec本身了解,更重要的是了解OpenCL硬件设备的特性,现阶段来说,主要是了解GPU的的架构特性,这样才能针对硬件特性优化算法。现在OpenCL的Spec是1.1,随着硬件的发展,相信OpenCL会支持更多的并行计算特性,基于OpenCL的并行计算才刚刚起步。

    1、Workgroup到硬件线程

    image

         在OpenCL中,Kernel函数被workgroup中的workitem(线程,我可能混用这两个概念)执行。在硬件层次,workgroup被映射到硬件的cu(compute unit)单元来执行具体计算,而cu一般由更多的SIMT(单指令,线程)pe(processing elements)组成。这些pe执行具体的workitem计算,它们执行同样的指令,但操作的数据不一样,用simd的方式完成最终的计算。

        由于硬件的限制,比如cu中pe数量的限制,实际上workgroup中线程并不是同时执行的,而是有一个调度单位,同一个workgroup中的线程,按照调度单位分组,然后一组一组调度硬件上去执行。这个调度单位在nv的硬件上称作warp,在AMD的硬件上称作wavefront,或者简称为wave。

    image

      上图显示了workgroup中,线程被划分为不同wave的分组情况。wave中的线程同步执行相同的指令,但每个线程都有自己的register状态,可以执行不同的控制分支。比如一个控制语句

    if(A)

    {

    … //分支A

    }

    else

    {

      … //分支B

    }

        假设wave中的64个线程中,奇数线程执行分支A,偶数线程执行分支B,由于wave中的线程必须执行相同的指令,所以这条控制语句被拆分为两次执行[编译阶段进行了分支预测],第一次分支A的奇数线程执行,偶数线程进行空操作,第二次偶数线程执行,奇数线程空操作。硬件系统有一个64位mask寄存器,第一次是它为01…0101,第二次会进行反转操作10…1010,根据mask寄存器的置位情况,来选择执行不同的线程。可见对于分支多的kernel函数,如果不同线程的执行发生diverage的情况太多,会影响程序的性能。

    2、AMD wave调度

    image

       AMD GPU的线程调度单位是wave,每个wave的大小是64。指令发射单元发射5路的VLIW指令,每个stream core(SC)执行一条VLIW指令,16个stream core在一个时钟周期执行16条VLIW指令。每个时钟周期,1/4wave被完成,整个wave完成需要四个连续的时钟周期。

        另外还有以下几点值得我们了解:

    • 发生RAW hazard情况下,整个wave必须stall 4个时钟周期,这时,如果其它的wave可以利用,ALU会执行其它的wave以便隐藏时延,8个时钟周期后,如果先前等待wave已经准备好了,ALU会继续执行这个wave。
    • 两个wave能够完全隐藏RAW时延。第一个wave执行时候,第二个wave在调度等待数据,第一个wave执行完时,第二个wave可以立即开始执行。

    3、NV warp调度

    image

         work group以32个线程为单位,分成不同warp,这些warp被SM调度执行。每次warp中一半的线程被发射执行,而且这些线程能够交错执行。可以用的warp数量依赖于每个block的资源情况。除了大小不一样外,wave和warp在硬件特性上很相似。

    4、Occupancy开销

        在每个cu中,同时激活的wave数量是受限制的,这和每个线程使用register和local memory大小有关,因为对于每个cu,register和local memory总量是一定的。

        我们用术语Occupancy来衡量一个cu中active wave的数量。如果同时激活的wave越多,能更好的隐藏时延,在后面性能优化的章节中,我们还会更具体讨论Occupancy。

    5、控制流和分支预测(prediction)

       前面我说了if else的分支执行情况,当一个wave中不同线程出现diverage的时候,会通过mask来控制线程的执行路径。这种预测(prediction)的方式基于下面的考虑:

    • 分支的代码都比较短
    • 这种prediction的方式比条件指令更高效。
    • 在编译阶段,编译器能够用predition替换switch或者if else。

      prediction 可以定义为:根据判断条件,条件码被设置为true或者false

    __kernel 
    void test() {

    int tid= get_local_id(0) ;
    if( tid %2 == 0)
    Do_Some_Work() ;
    else
    Do_Other_Work() ;
    }

    例如上面的代码就是可预测的,

    Predicate = True for threads 0,2,4….

    Predicate = False for threads 1,3,5….

    下面在看一个控制流diverage的例子

    image

    • 在case1中,所有奇数线程执行DoSomeWork2(),所有偶数线程执行DoSomeWorks,但是在每个wave中,if和else代码指令都要被发射。
    • 在case2中,第一个wave执行if,其它的wave执行else,这种情况下,每个wave中,if和else代码只被发射一个。

    image

    在prediction下,指令执行时间是if,else两个代码快执行时间之和。

    6、Warp voting

       warp voting是一个warp内的线程之间隐式同步的机制。

    image

        比如一个warp内线程同时写Local meory某个地址,在线程并发执行时候,warp voting机制可以保证它们的前后顺序正确。更详细的warp voting大家可以参考cuda的资料。>

      

        在OpenCL编程中,由于各种硬件设备不同,导致我们必须针对不同的硬件进行优化,这也是OpenCL编程的一个挑战,比如warp和wave数量的不同,使得我们在设计workgroup大小时候,必须针对自己的平台进行优化,如果选择32,对于AMD GPU,可能一个wave中32线程是空操作,而如果选择64,对nv GPU来说,可能会出现资源竞争的情况加剧,比如register以及local meomory的分配等等。这儿还不说混合CPU device的情况,OpenCL并行编程的道路还很漫长,期待新的OpenCL架构的出现。

  • 相关阅读:
    SysTick—系统定时器
    FreeRtos——单任务
    binutils工具集之---objdump
    对连接器的思考
    数组和指针并不相同
    typedef可以成为你的朋友
    gcc,一个神奇的编译器
    FreeRtos——移植
    Makefile 13——理解make的解析行为
    Makefile 12——改善编译效率
  • 原文地址:https://www.cnblogs.com/mikewolf2002/p/2333903.html
Copyright © 2011-2022 走看看