CUDA上的并行归约与索引查找

CUDA上的并行归约与索引查找,cuda,Cuda,我有一个20K值的数组,我将它减少到50个块,每个块有400个线程。块数=50,块大小=400 我的代码如下所示: getmax <<< num_blocks,block_size >>> (d_in, d_out1, d_indices); __global__ void getmax(float *in1, float *out1, int *index) { // Declare arrays to be in shared memory.

我有一个20K值的数组,我将它减少到50个块,每个块有400个线程。块数=50,块大小=400

我的代码如下所示:

getmax <<< num_blocks,block_size >>> (d_in, d_out1, d_indices);

__global__ void getmax(float *in1, float *out1, int *index)
{
    // Declare arrays to be in shared memory.
    __shared__ float max[threads];

    int nTotalThreads = blockDim.x;    // Total number of active threads
    float temp;
    float max_val;
    int max_index;
    int arrayIndex;

    // Calculate which element this thread reads from memory
    arrayIndex = gridDim.x*blockDim.x*blockIdx.y + blockDim.x*blockIdx.x + threadIdx.x;
    max[threadIdx.x] = in1[arrayIndex];
    max_val = max[threadIdx.x];
    max_index = blockDim.x*blockIdx.x + threadIdx.x;
    __syncthreads();

    while(nTotalThreads > 1)
    {
        int halfPoint = (nTotalThreads >> 1);
        if (threadIdx.x < halfPoint) 
        {
            temp = max[threadIdx.x + halfPoint];
            if (temp > max[threadIdx.x]) 
            {
                max[threadIdx.x] = temp;
                max_val = max[threadIdx.x];            
            }
        }
        __syncthreads();

        nTotalThreads = (nTotalThreads >> 1);    // divide by two.
    }

    if (threadIdx.x == 0)
    {
        out1[num_blocks*blockIdx.y + blockIdx.x] = max[threadIdx.x];
    }

    if(max[blockIdx.x] == max_val )
    {
        index[blockIdx.x] = max_index;    
    }
}
getmax>(数据输入、数据输出1、数据索引);
__全局无效getmax(浮点*in1,浮点*out1,整数*index)
{
//声明数组在共享内存中。
__共享_uuuu浮点最大值[线程];
int ntotatalThreads=blockDim.x;//活动线程的总数
浮子温度;
浮动最大值;
int max_指数;
数组索引;
//计算此线程从内存中读取的元素
arrayIndex=gridDim.x*blockDim.x*blockIdx.y+blockDim.x*blockIdx.x+threadIdx.x;
max[threadIdx.x]=in1[arrayIndex];
max_val=max[threadIdx.x];
max_index=blockDim.x*blockIdx.x+threadIdx.x;
__同步线程();
而(线程数>1)
{
int halfPoint=(nTotalThreads>>1);
if(螺纹IDX.x<半点)
{
温度=最大值[螺纹内径x.x+半点];
如果(温度>最大值[threadIdx.x])
{
最大[threadIdx.x]=温度;
max_val=max[threadIdx.x];
}
}
__同步线程();
nTotalThreads=(nTotalThreads>>1);//除以二。
}
if(threadIdx.x==0)
{
out1[num_blocks*blockIdx.y+blockIdx.x]=max[threadIdx.x];
}
if(max[blockIdx.x]==max_val)
{
索引[blockIdx.x]=最大索引;
}
}
这里的问题是,在某种程度上,“nTotalThreads”并不是2的幂,这会导致索引的垃圾值。数组out1给出了每个块中的最大值,该值是正确且经过验证的。但该指数的值是错误的。例如:第一个块中的最大值出现在index=40时,但内核给出的index值为15。类似地,第二个块中的max值为440,但内核给出416


有什么建议吗?

你确定你真的需要这个“问题”吗?“nTotalThreads”不是2的幂? 这会降低代码的可读性,我认为这也会影响性能。 无论如何,如果你替换

nTotalThreads=(nTotalThreads>>1)

nTotalThreads=(nTotalThreads+1)>>1

它应该解决一个关于这个“问题”的bug


弗朗西斯科赞同杰夫的建议


看一看,与手工调优的内核相比,它的效率高达95%,而且非常灵活,易于使用。

应该很容易确保nTotalThreads始终是2的幂

将第一次缩减作为一个特例,使nTotalThreads的幂为2。例如,由于您在一个块中以400个线程开始,因此使用256个线程进行第一次缩减。线程0-199将从两个值减少,而线程200-255不必在这个初始步骤中进行减少。从那以后你就没事了。

看看我的。您可以将blockresults放入数组(可以在全局内存中),并在全局内存中获得结果

看看我如何在主机代码中调用它:

sumSeries<<<dim3(blockCount),dim3(threadsPerBlock)>>>(deviceSum,threadsPerBlock*blockCount);
sumSeries(deviceSum,threadsPerBlock*blockCount);

许多常见模式(如并行缩减)都是在CUDA的高度优化库中实现的,如推力或CUDPP,您有没有为您的任务查看过这些模式?如果您不介意的话,为什么每个块有400个线程?