C++ 平行还原

C++ 平行还原,c++,c,cuda,parallel-processing,gpu,C++,C,Cuda,Parallel Processing,Gpu,我读过Mark Harris的文章《优化CUDA中的并行归约》,我发现它确实非常有用,但有时我还是无法理解一两个概念。 它写在第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[ti

我读过Mark Harris的文章《优化CUDA中的并行归约》,我发现它确实非常有用,但有时我还是无法理解一两个概念。 它写在第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

我无法理解第二行;如果我有256个元素,如果我选择128作为块大小,那么为什么我要将它乘以2?请解释如何确定块大小?

在优化代码中,运行内核时,块的大小是非优化实现中的一半

让我们调用非优化代码中块的大小
work
,将此大小的一半称为
unit
,并使这些大小对于优化代码也具有相同的数值

在非优化代码中,运行内核的线程数与
工作线程数相同,即
blockDim=2*unit
。每个块中的代码只是将
g_idata
的一部分复制到共享内存中大小为
2*单位的数组中

在优化的代码中,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]

基本上,它正在执行下图所示的操作:

这段代码基本上是说,一半的线程将执行从全局内存读取和写入共享内存的操作,如图所示

执行一个内核,现在要减少一些值,将对上述代码的访问限制为运行线程总数的一半。假设您有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];
因此:


之所以使用
blockDim.x*2
,是因为每个线程将访问位置
i
i+blockDim.x
,因此您需要乘以
2
,以保证下一个
id
块上的线程不会计算已经计算的
g_idata
的位置。

@p Marecki:非常感谢。你的回答确实帮助我理解这个解决方案,但是如果你能让我知道在第一段中,总的元素是什么。如果是256,那么单个块将如何占用256个元素?对于第二段512个元素和只有2个128个线程的块,同样的问题。@robot:g_idata中的元素总数在第1段为256个,在第二段为512个。正确:在优化代码中,
sdata
小于
2x
元素(您只有
128
元素,或者第二段中的
2*128
),但这就足够减少了。@P Marecki:谢谢您的回复。但是如果有256个元素,那么我们必须处理256个元素,如何将元素减少到128个元素?你的意思是有256个元素,我们有一个块大小为128的块来处理256个元素?@P Marecki:是不是:优化代码中的块数应该比非优化代码中的块数减少一半。您已经编写了512个元素,最初有256个线程/块的块,在优化代码中有128个线程/块;那么,我们在哪里将街区数量减半?@robot:我想我理解了混乱的根源。在编辑后的答案中,现在有变量
work
unit
,这两个变量在两种实现中具有相同的数值,而
blockDim
在这两种实现中是不同的。
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