cuda 优化实例--计算平方和

平方和程序的渐进优化

  1. 当线程访问global memory没有顺序访问
  2. 线程顺序访问global memory
  3. 在1的基础之上增加并行计算规模,也就是使用多个block
    如何选择最优的的线程配置,可能影响线程配置的因素有一下几个:
    • 为了高效地使用寄存器,每个sm上至少要有192个激活的线程
    • 为了将bank冲突将到最低,应尽量使每个block含有的线程数是64的倍数
    • 当某个block中的线程被同步时候,为了避免对应的sm出现闲置,通常让block的数量是sm的2倍以上,以便使得处于不同状态的(闲置或者不闲置)block在时间上重叠。
    • block中的线程数应该设置成warp尺寸的整数倍,并且不超过最大线程数的规定。
    • 一个block中设置的线程过多会导致每个线程可利用的寄存器变少,单寄存器变少的时候会因为访问溢出寄存器数据(溢出的数据在设备存储器中)而变得缓慢。甚至调用失败。
  4. 在2的基础之上使用shared memory进行GPU内部求和, cpu的计算量为THREAD_NUM * BLOCK_NUM 为了减少cpu在规约上的时间,使用shared memory对每个block求和,再在cpu端进行BLOCK_NUM数量的求和.
  5. 3中一个block内由单个线程完成求和,同时其他线程闲置,导致计算效率低。使用缩减树算法可以提高计算效率。
    • 理解缩减树算法中的mask、offset的概念
  6. 避免共享存储的bank冲突,一旦使用了共享存储就要想到如何避免bank冲突的问题。
    • 共享存储器由以四个字节为单位分成16个存储器组组成。
    • 对共享存储器的访问是以半个warp为单位访问的,当半warp中的每个线程访问的数组元素分属与不同的bank时候不会产生冲突。而半warp中的多个线程访问的数组元素处于同一个bank的时候,bank的访问就会串行化。
    • 为了避免bank冲突,通常需要对共享存储器进行补白(data padding)
  7. 减少指令开销。GPU在单核的计算能力远不如CPU强悍,CPU具有复杂的存储器缓存系统从而能保持极低的缓存误访问率,以及先进的指令缓存系统和强大的分支预测能力。GPU的标量处理器比较简单,太多复杂冗长的指令会明显降低GPU的计算性能。GPU适合于少循环、少跳转的语句。
    指令开销通常可以分成三种:
    • 控制指令:指的是循环语句,条件语句等。控制指令对于程序性能的影响主要产生于warp内,同一warp的线程总是被分配执行相同的指令,如果控制指令造成不同的线程执行路径处于不同的分支,则这些不同的执行分支需要被串行化。
      6-sos.cu中将while展开为多个条件语句来减少循环。(为啥展开之后反而变慢了。。。。)
    • 同步指令:cuda的同步指令分成针对时间的同步和对存储单元的同步。时间同步是指某个范围内线程在完成制定任务后,才能继续执行的命令;存储单元的同步,是指通过广播的形式告知某个范围内的线程某个变量已经改变,或强制在访问变量时重新读取变量。
      cuda内的同步方式可以分成:
      (1)同步block内的线程,即__syncthreads(),注意的是一个warp内的线程无需同步。
      (2)memory fence:保证存储器的线程安全
      • __threadfence():针对grid的所有线程
      • __threadfece_block():针对block内的线程
    • 算术指令:利用cuda提供的标准数学函数库可以提高计算效率。当前较为常用的加速库是blas。

你可能感兴趣的:(CUDA编程)