平行减少

时间:2012-11-29 08:29:56

标签: c++ c cuda parallel-processing gpu

我已经读过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?请解释如何确定块大小?

2 个答案:

答案 0 :(得分:8)

它基本上执行如下图所示的操作:

enter image description here

这段代码基本上是“说”半数线程将从全局内存中读取并写入共享内存,如图所示。

您执行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是因为每个线程都将访问位置ii+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=256unit=128)进行非优化,则优化代码有2个块,但现在大小为128.第一个线程在第二个块(blockIdx=1)中g_idata[2*128] + g_idata[2*128+128]