硬件
SP(Streaming Processor)
:流处理器,是GPU最基本的处理单元,在fermi
架构开始被叫做CUDA core
。SM(Streaming MultiProcessor)
:一个SM由多个CUDA core
组成。
比如说,如果一个GPU有 $4$ 个SM
,并且每个SM
有 $768$ 个SP
(Aka CUDA core
);那么在某一时刻,真正并行运行的线程数不会超过 $4 \times 768$ 个。
软件
threads
被组织成blocks
。一个block
的线程可以用1Dimension(x)
, 2Dimensions(x, y)
或者3Dim indexs(x, y, z)
索引,
显然,如果你需要 $4 \times 768$ 个以上的threads
的话你需要 $4$ 个以上的blocks
。blocks
也可以使用1D
, 2D
或3D
索引,这些blocks
被放在等待队列上进入GPU执行。
Wrap
当一个kernel
被执行时,grid
中的线程块被分配到SM
上。一个CUDA core
可以执行一个thread
,一个SM
的CUDA core
会分成几个wrap
,由wrap scheduler
负责调度。
一个wrap
中的线程在同一个block
中,如果block
所含线程数不是wrap
的大小的整数倍,那么多出来的那些thread
所在的wrap
中,会剩余一些inactive
的thread
。
一个简单的case
处理一张 $512 \times 512$ 的图片。
假设我们希望一个线程处理一个像素pixel(i, j)
。
我们可以使用每 $64$ 个线程的区块。所以我们需要 $\frac{512 \times 512 }{64} = 4096$ 个区块(为了拥有 $512 \times 512 $ 个线程 )。
通常情况下,我们将线程组织在2D
区块中(为了更容易索引图像像素)。blockDim
= $8 * 8$ ,我更喜欢叫它threadsPerBlock
。
dim3 threadsPerBlock(8, 8);
还有2D
的gridDim
= $64 \times 64$ (需要 $4096$ 个区块)。我更喜欢叫它numBlocks
。
dim3 numBlocks(imageWidth/threadPerBlock.x, imageHeight/threadPerBlock.y);
这个kernel
是这样启动的:
myKernel <<<numBlocks, threadsPerBlock>>>(/* params for the kernel function */)
最后,会有一个类似 $4096$ 个区块的队列的东西,其中一个块正在等待被分配到GPU的一个多处理单元中,以获得其 $64$ 个线程的执行。
在kernel
中,一个线程要处理的像素是这样计算的:
uint i = (blockIdx.x + blockDim.x) + threadIdx.x;
uint j = (blockIdx.y + blockDim.y) + threadIdx.y;