一 测量程序运行时间
1主机端测时
由于CUDA API函数都是异步的,为了准确测量CUDA调用运行的时间,首先要使用cudaThreadSynchronize(),同步cpu与gpu之后,才能结束测时。
2设备端测时
使用clock()函数,这个函数测的结果是一个block在gpu中上下文保持的时间,而不是实际执行的时间。(一般大于执行时间)
二任务划分
1原则
划分为cpu端程序和gpu端程序
选择合适的算法
在两次主机设备通信之间进行尽量多的计算
使用流运算隐藏主机-设备通信时间,以及通过pinned mem,zero-copy,write-combined mem等提高实际传输带宽。
2grid和block维度设计
tesla架构gpu中的每个sm中,至少需要6个active warp才能有效隐藏流水线延迟。如果所有active warp来自同一block,那么当block进行存储器访问或者同步,执行单元就会闲置。
所以每个sm最好2个active block。
设计步骤:
计算出一个sm上active block和active warp的数目。可以通过CUDA SDK 中的CUDA occupany calculator计算。
指导原则:
(1)每个block中线程数量是32的整数倍,最好保持64-256之间
(2)blockDim.x为16或者16整数倍,提高对global mem 和 shared mem的访问效率。
三 存储器访问优化
1主机设备通信优化
(1)使用pinned memory----主机内存不会被分配到虚拟内存中去
(2)异步执行内核函数和异步存储拷贝函数都是立刻返回的。
方法一:使用流和异步使GPU和CPU同时进行计算。
方法二:利用不同流之间的异步执行,使流之间的传输和运算能够同时执行。
2全局存储器访问优化
对全局内存的访问满足合并条件是对cuda程序性能影响最明显的因素之一。
通过运行时API(如cudamalloc())分配的存储器,已经能保证棋手地址至少会按256Byte进行对齐。因此,选择合适的线程块大小(例如16的倍数),使得half-warp的访问请求按段长对齐。
3共享存储器的访问优化在不发生bank conflict的情况下,share mem延迟是local mem 和 global mem的1/100,与寄存器相同。
对于计算能力1.x的设备,warp大小32,而一个sm的shared mem划分为16个bank,一个warp访问share mem会被划分为两个half-warp的访问请求。所以只有处于同一half-warp的线程才会发生bank-confict,而一个warp中前hal-warp和位于后half-warp的线程间则不会发生bank confilct.
-shared- float shared[32];
float data=shared[baseindex+s*tid];
对于计算能力1.x的硬件,s为奇数,就可以避免bank conflict的发生。(因为bank数目是2的幂)
对于double数组访问存在2-way冲突,此时访存被编译为两个独立的32bit请求。
-shared-double shared[32];
double data=shared[baseindex+tid];
shared mem采用了广播机制,当对同一个地址的读请求时,一个32bit字可以被读取并同时广播给不同的进程。
四指令流优化
使用高速的函数