CUDA编程学习笔记(三)

        本篇记录学习cuda的执行模式,主要有几个方面的内容:

                理解jetson nano上的gpu架构;

                理解线程束执行的本质;

                分支化与避免分支化; 

                展开循环;

                其他;

一、理解jetson nano的gpu架构

        这块内容在上一篇笔记中有学习到一些,本篇就进行一个补充和复习。

        jetson nano上的gpu是Maxwell架构的,整个gpu上面只有一个SM(流多处理器)。该SM上包含128个处理核心,这128个计算核心被划分为4个部分,每一个部分包含32个计算核心,这和warp调度有着密切联系,因为32个计算核心刚好能够同时执行一个warp。

        另外,我在网上查询数据后,了解到jetson nano上总共的内存只有4G,我本以为这4G是cpu和gpu共享的内存,从而不需要进行显示的数据传输(使用cudaMalloc,cudaFree,cudaMemcopy等api),但上手操作了一下,发现仍然需要进行显示传输。

二、理解线程束执行的本质

        我们在编写一个kernel后,会根据需要处理的数据量的大小来组织线程,分配合理的线程数量来执行核函数。线程数量管理和组织可以被理解为一个层次结构,即grid、block、thread。

        dim3 gridsize(2,3)

        dim3 blocksize(16,16)

        上面两行代码表示将所有线程组织为一张2行3列的网格,该网格由线程块组成,所以整个网格包含2*3=6个线程块;往下一个层次走,每一个线程块被组织为了16行16列,即表示一个线程块有16*16=256个线程。经过这样划分,那么整个网格就包含6*256=1536个线程。最后就表示经过这样组织,这个核函数最多可以使用计算核心执行1536次运算。

        很明显,jetson nano上只有一个SM,并且只有128个计算核心。对于一个SM上一次只能装载一个block,每个SM同时最多只能执行4个warp,那么整个执行过程是怎样的呢?

        上一篇已经学习过了warp的概念,其实就是两句话:一个warp由32个线程组成,这32个线程同时执行,同时结束;warp是gpu执行的最小单位。

        很多其他架构的gpu不止一个SM,加入有n和SM,那么多个block可以同时装载到n个SM上进行调度。但本篇是针对jetson nano进行分析,针对只有一个SM的架构。

        所以,在jetsonnano上,不管你网格是怎么组织的,所有组织好的线程块都会由硬件来进行调度,一个block会被装载到SM上,后面的block就需要进行等待。按照上面的例子,一个block有256个线程,但SM上只有128个线程,怎么进行调度的呢?这个时候就需要利用到warp的概念了。因为SM被划分为了4个组,每一个包含32个计算核心,所以被调度到SM上的block所包含的256个线程会被划分为256/32=8个warp,这8个warp就会被调度到这4个计算核心组上进行计算。

三、线程束分化、避免分化

        一个warp当中的线程是并行的,并且是SIMD执行的,所以一个warp当中的线程执行的是相同的指令。但是,如果在kernel中出现了if判断等分支语句的导师一个warp中的线程出现了多种执行路线,导致同一时刻部分线程执行的不是同一个指令怎么办呢?这其实就是线程束分化。

        线程束分化其实就是同一个warp中的线程执行了不同的指令。这样会导致一中情况,并行变串行,性能下降。

        假设一个warp内的线程前16个1线程执行+1操作,后16个线程执行-1操作,那么就会导致线程束分化。这就会造成前16个线程先去执行+1操作,而后16个线程等待,等到前16个线程执行完毕后,后16个线程执行-1操作,等到后16个线程执行完毕后,整个warp才调度出去。

        所以就导致了性能降低,原因是不分县城从并行变为了串行,没有充分使用到整个warp的32个计算核心。

        其实解决线程束分化的方法也很简单,就是让处于一个分支的线程出于一个warp中。但具体处理数据需要具体分析,怎样让执行相同分支的在一个warp需要用到不同的方法。

四、循环展开

        和之前学习arm neon 时一样,在对循环进行展开时,其实是为了减少检查判断的省略。在汇编语言层面,会将展开的循环语句也按照汇编语言展开,这样就减少了循环的判断和跳转。

五、其他

        补充记录一下关于sm。

       在jetson nano上面的资源非常有限,该平台上只有一个sm,通过一下代码可以获取到该sm上运行线程和线程块的信息。

        int device;
        cudaGetDevice(&device);

        cudaDeviceProp prop;
        cudaGetDeviceProperties(&prop, device);

        int maxBlocksPerSM = prop.maxThreadsPerMultiProcessor / prop.maxThreadsPerBlock;
        printf("maxThreadsPerMultiProcessor:%d\n",prop.maxThreadsPerMultiProcessor);
        printf("maxThreadsPerBlock:%d\n",prop.maxThreadsPerBlock);
        printf("maxBlocksPerSM:%d\n",maxBlocksPerSM);

        可以看到,其实一个sm上能够运行的线程和线程块其实是有限的,在jetson nano上一个sm最多只能装载两个block,即最多有两个block在并行执行,而一个block最多包含1024个线程,所以我们可以在组织线程的时候,可以根据最大限制充分利用gpu进行计算。

        当然,影响执行效率的因素还有很多,比如寄存器和共享内存的使用,当核函数内部使用加多寄存器时,或者说使用共享内存使用加多时,都会导致装载到sm上的线程块减少。

        

  • 6
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值