GPU高性能计算CUDA编程:使用寄存器交换4个像素
声明:本文不做商用
在制定下一步改进计划时,我们将保留那些已经起作用的并改进那些还没有提升的地方。在Hflip7()中,我们通过读取3个自然的int大小的元素来读取4个像素的方式非常完美。该核函数的问题是,一旦进人共享内存,我们仍然按照unsignedchar的大小(字节)来处理数据。从共享内存(或任何类型的内存)中读取和写人这些非自然大小元素的操作效率非常低。在核函数Hflip7()中,每个字节交换都需要访问3次共享内存,每次都是非自然的 unsigned char 大小。
代码10.3中所示的Hflip8()的想法是将非自然大小的访问操作放在核心内部进行,因为核心在操作字节大小的数据元素时效率更高。为此,Hfip8()分配了6个变量A、B、C、D、E、F,它们都是具有自然大小的unsignedint(32位)类型。我们不再使用共享内存。对全局存储器(GM)的唯一访问是从它那儿以32位大小读取数据,如下所示:
ui A, B, C, D, E, F;
// read 4 pixel blocks (12B = 3 int's) into 3 long registers
A = ImgSrc32[MYsrcIndex];
B = ImgSrc32[MYsrcIndex + 1];
C = ImgSrc32[MYsrcIndex + 2];
这是将翻转后的像素写回GM之前的唯一一次GM访问。需要注意的是GM中的数据以小端格式存储,与共享内存相反。参见【0voice C++】所以,Hflip8()的目标是将如下所示的A、B、C
// 现在: A=[B1,R0,G0,B0] B=[G2,B2,R1,G1] C=[R3,G3,B3,R2]
转换为如下所示的 D、E、F:
// 我们的目标: D=[B2,R3,G3,B3] E=[G1,B1,R2,G2] F=[R0,G0,B0,R1]
这种方法高效的原因在于,由于核函数中需要的变量比较少,因此编译器可以轻松地将所有这些变量映射到核心寄存器中,这使得对它们的操作非常高效。此外,在字节的处理过程中,核心使用的只是shif、AND、OR操作,它们是核心中ALU的基本操作,可以由编译器选择编译为最快的指令。
举个例子,让我们分析下面的C语句:
// 现在: A=[B1,R0,G0,B0] B=[G2,B2,R1,G1] C=[R3,G3,B3,R2]
E = (B << 24) | (B >> 24) | ((A >> 8) & 0x00FF0000) | ((C << 8) & 0x0000FF00);
这里有4个桶式shif/AND运算产生如下的32位值:
(B<<24) = [G1,0,0,0] (B>>24) = [0,0,0,G2]
((A>>8) & 0x00FF0000) = ([0,B1,RO,G0] & [00,FF,00,00]) = [0,B1, 0, 0]
((C<<8) & 0x0000FF00) = ([G3,B3,R2,0] & [00,00,FF,00]) = [0,0, R2, 0]