我已经读过Mark Harris的文章“优化CUDA中的并行缩减”,我发现它非常有用,但我仍然无法理解1或2个概念。 它写在第18页:
//First add during load
// each thread loads one element from global to shared mem
unsigned int tid = threadIdx.x;
unsigned int i = blockIdx.x*blockDim.x + threadIdx.x;
sdata[tid] = g_idata[i];
__syncthreads();
优化代码:使用2次加载和第一次添加减少:
// perform first level of reduction,
// reading from global memory, writing to shared memory
unsigned int tid = threadIdx.x; ...1
unsigned int i = blockIdx.x*(blockDim.x*2) + threadIdx.x; ...2
sdata[tid] = g_idata[i] + g_idata[i+blockDim.x]; ...3
__syncthreads(); ...4
我无法理解第2行;如果我有256个元素,如果我选择128作为我的块大小,那么为什么我将它乘以2?请解释如何确定块大小?
答案 0 :(得分:8)
它基本上执行如下图所示的操作:
这段代码基本上是“说”半数线程将从全局内存中读取并写入共享内存,如图所示。
您执行Kernell,现在要减少某些值,将上面代码的访问限制为仅运行的线程总数的一半。想象你有4个块,每个块有512个线程,你将上面的代码限制为仅由两个第一个块执行,并且你有一个g_idate [4 * 512]:
unsigned int i = blockIdx.x*(blockDim.x*2) + threadIdx.x;
sdata[tid] = g_idata[i] + g_idata[i+blockDim.x];
所以:
thread 0 of block = 0 will copy the position 0 and 512,
thread 1 of block = 0 position 1 and 513;
thread 511 of block = 0 position 511 and 1023;
thread 0 of block 1 position 1024 and 1536
thread 511 of block = 1 position 1535 and 2047
使用blockDim.x * 2是因为每个线程都将访问位置i
和i+blockDim.x
,因此您需要多次2来保证下一个id块上的线程不与{的相同位置重叠已经计算过{。}}。
答案 1 :(得分:0)
在优化的代码中,运行内核的块数是非优化实现的一半。
让我们在非优化代码work
中调用块的大小,将这个大小的一半称为unit
,并让这些大小的优化代码具有相同的数值。
在非优化代码中,运行内核的线程与work
一样多,即blockDim = 2 * unit
。每个块中的代码只是将g_idata
的一部分复制到共享内存中的大小为2 * unit
的数组中。
在优化代码blockDim = unit
中,现在有1/2个线程,共享内存中的数组小2倍。在第3行中,第一个加数来自偶数单位,而第二个来自奇数单位。通过这种方式,可以考虑减少所需的所有数据。
实施例:
如果使用blockDim=256=work
(单个块,unit=128
)运行非优化内核,则优化代码只有blockDim=128=unit
个块。由于此块获得blockIdx=0
,因此*2
无关紧要;第一个线程g_idata[0] + g_idata[0 + 128]
。
如果您有512个元素,并且使用2个256大小的块(work=256
,unit=128
)进行非优化,则优化代码有2个块,但现在大小为128.第一个线程在第二个块(blockIdx=1
)中g_idata[2*128] + g_idata[2*128+128]
。