通过cuda内核减少找到最小值和最大值

时间:2013-04-20 16:35:12

标签: cuda

这是我的内核代码

typedef unsigned char Npp8u;
...
    // Kernel Implementation
__device__ unsigned int min_device;
__device__ unsigned int max_device;


__global__ void findMax_Min(Npp8u * data, int numEl){
    int index = blockDim.x*blockIdx.x + threadIdx.x;
    int shared_index = threadIdx.x;

    __shared__ Npp8u data_shared_min[BLOCKDIM];
    __shared__ Npp8u data_shared_max[BLOCKDIM];

    // check index condition
    if(index < numEl){
        data_shared_min[shared_index] = data[index]; //pass values from global to shared memory
        __syncthreads();
        data_shared_max[shared_index] = data[index]; //pass values from global to shared memory


        for (unsigned int stride = BLOCKDIM/2; stride > 0; stride >>= 1) {
            if(threadIdx.x <  stride){
                if(data_shared_max[threadIdx.x] <  data_shared_max[threadIdx.x+stride]) data_shared_max[shared_index] = data_shared_max[shared_index+stride];
                if(data_shared_min[threadIdx.x]>  data_shared_min[threadIdx.x+stride]) data_shared_min[shared_index] = data_shared_min[shared_index+stride];
            }
            __syncthreads();
        }
        if(threadIdx.x == 0  ){
            atomicMin(&(min_device), (unsigned int)data_shared_min[threadIdx.x ]);
            //min_device =10;
            __syncthreads();
            atomicMax(&(max_device), (unsigned int)data_shared_max[threadIdx.x ]);
        }
    }else{
        data_shared_min[shared_index] = 9999;
    }
}

我的图像是512x512,我想找到最小和最大像素值。 data是图像的1-D版本。此代码适用于最大值,但不适用于最小值。当我从matlab检查时,最大值为202,最小值为10,但是最小值为0。这是我的内核代码和memcpy调用

int main(){
    // Host parameter declarations.
    Npp8u * imageHost;
    int   nWidth, nHeight, nMaxGray;

    // Load image to the host.
    std::cout << "Load PGM file." << std::endl;
    imageHost = LoadPGM("lena_before.pgm", nWidth, nHeight, nMaxGray);

    // Device parameter declarations.
    Npp8u    * imageDevice;
    unsigned int   max, min;
    size_t size = sizeof(Npp8u)*nWidth*nHeight;

    cudaMalloc((Npp8u**)&imageDevice, size);

    cudaMemcpy(imageDevice, imageHost, size, cudaMemcpyHostToDevice);

    int numPixels = nWidth*nHeight;

    dim3 numThreads(BLOCKDIM);
    dim3 numBlocks(numPixels/BLOCKDIM + (numPixels%BLOCKDIM == 0 ? 0 : 1));

    findMax_Min<<<numBlocks, numThreads>>>(imageDevice,numPixels);
    cudaMemcpyFromSymbol(&max,max_device, sizeof(max_device), 0, cudaMemcpyDeviceToHost);
    cudaMemcpyFromSymbol(&min,min_device, sizeof(min_device), 0, cudaMemcpyDeviceToHost);


    printf("Min value for image : %i\n", min);
    printf("Max value for image : %i\n", max);
...

另一个有趣的事情是在内核调用导致故障并且值都被读为零之后改变cudaMemcpy的顺序。我没有看到问题。有没有人看到被遮挡的部分?

1 个答案:

答案 0 :(得分:1)

您可能想要进行cuda错误检查。您可能还希望将min_device初始化为较大的值,将max_device初始化为零。与stride相关的缩减方法还有其他问题(当你向threadIdx.x添加步幅时,在奇数大小图像的最后一个块中会发生什么,它可能会超出共享内存中定义的图像范围),但我不认为这对512x512的图像很重要。如果min_device恰好从零开始,那么所有的atomicMin操作都会在那里保持为零。

您可以尝试初始化min_devicemax_device,如下所示:

__device__ unsigned int min_device = 9999;
__device__ unsigned int max_device = 0;

对于最后的cudamemcpy调用,您将4个字节(max_device的大小)复制为单字节变量(Npp8u max),同样为min。这是一个问题。由于您正在使用指针,因此复制操作肯定会覆盖您不想要的内容。如果编译器按照您定义的方式顺序存储变量,则一个复制操作将覆盖另一个变量,我认为这将解释您所看到的行为。如果您将minmax创建为unsigned int数量,我认为这个问题会消失。

编辑:由于您尚未显示实际的块尺寸,因此您可能仍然存在缩减问题。您可能想要更改此行:

        if(threadIdx.x <  stride){

类似于:

        if((threadIdx.x <  stride) && ((index + stride)< numEl)){

这个或类似的东西应该纠正我在第一段中提到的危险。我想你正试图用这条线来解释危险:

    data_shared_min[shared_index] = 9999;

但是,无法保证在共享内存中设置的数据元素被其他某个线程读取之前,代码行会被执行。我也不知道在为字节数量分配值9999时会发生什么,但它可能不是您所期望的。