你的代码是100%正确的。问题是,位移不占数组的最后一部分。您可以通过人工将数组扩展到2的下一个幂来轻松解决这个问题。这样你的整个数组就会减少,额外的“元素”(它们实际上并不存在)就会被忽略。
#include <math.h>
__global__ void reduction(float *g_data, int n){
// figure out exponent of next larger power of 2
int exponent = ceilf(log2f(n));
// calculate next larger power of 2
int size = (int)powf(2, exponent);
__shared__ float partialSum[NUM_ELEMENTS];
int tx = threadIdx.x;
int i = tx + blockIdx.x * blockDim.x;
if (i < n){
partialSum[tx] = g_data[i];
}
for (int stride = size / 2; stride > 0; stride >>= 1){
__syncthreads();
if (tx < stride) {
// all threads that run out of bounds do nothing
// equivalent to adding 0
if((tx + stride) < n)
partialSum[tx] += partialSum[tx + stride];
}
}
if (tx == 0){
g_data[blockIdx.x] = partialSum[tx];
}
}
编辑
关于您的注释,这种缩减方法永远不会适用于在多个块中缩减的数组。因此,对于计算能力1.0-1.3,可以减少的最大数组是512个元素,对于计算能力1.3,最多可以执行1024个元素,这是每个块的最大线程数。
这是因为
__shared__
记忆是
线程之间共享而不是块
. 因此,要减少分散在多个块上的数组,需要对数组进行分区,以便每个块减少一个块,然后利用
__global__
内存以减少所有块中的值。然而,
γ粒子
内存大约比(片上)慢10-20倍
第二股
内存,所以一旦你开始使用大量的块,这将变得非常低效。
另一种方法是让每个线程处理多个索引,但是,最终
partialSum
数组将不再适合共享内存,并溢出到全局内存。这种方法还意味着您永远不能使用超过512(或1024)个线程,这违背了使用CUDA的目的,CUDA依赖于运行大量线程来隐藏延迟,并使从主机到设备的昂贵内存传输值得。