0

我正在尝试将用 Cuda 编写的代码转换为 openCL 并遇到一些麻烦。我的最终目标是在带有 Mali T628 GPU 的 Odroid XU3 板上实现代码。

为了简化转换并节省尝试调试 openCL 内核的时间,我完成了以下步骤:

  1. 在 Cuda 中实现代码并在 Nvidia GeForce 760 上进行测试
  2. 在 openCL 中实现代码并在 Nvidia GeForce 760 上进行测试
  3. 在带有 Mali T628 GPU 的 Odroid XU3 板上测试 openCL 代码。

我知道不同的架构可能有不同的优化,但这不是我现在主要关心的问题。我设法在我的 Nvidia GPU 上运行 openCL 代码,没有明显问题,但是在尝试在 Odroid 板上运行代码时不断出现奇怪的错误。我知道不同的架构对异常等有不同的处理,但我不确定如何解决这些问题。

由于 openCL 代码在我的 Nvidia 上工作,我假设我设法在线程/块之间进行了正确的转换 - > workItems/workGroups 等。我已经修复了几个与 cl_device_max_work_group_size 问题相关的问题,所以这不是原因。

运行代码时,我收到“CL_OUT_OF_RESOURCES”错误。我已将错误原因缩小到代码中的 2 行,但不确定是否能解决这些问题。

该错误是由以下几行引起的:

  1. 最低距离[pixelNum] = partialDiffSumTemp; 这两个变量都是内核的私有变量,因此我没有看到任何潜在的问题。
  2. d_disparityLeft[globalMemIdx + TILE_BOUNDARY_WIDTH - WINDOW_RADIUS + 0] = bestDisparity[0]; 在这里我猜原因是“OUT_OF_BOUND”,但不知道如何调试它,因为原始代码没有任何问题。

我的内核代码是:

#define ALIGN_IMAGE_WIDTH          64
#define NUM_PIXEL_PER_THREAD        4

#define MIN_DISPARITY               0  
#define MAX_DISPARITY              55  

#define WINDOW_SIZE                19 
#define WINDOW_RADIUS              (WINDOW_SIZE / 2)   

#define TILE_SHARED_MEM_WIDTH      96                       
#define TILE_SHARED_MEM_HEIGHT     32
#define TILE_BOUNDARY_WIDTH        64
#define TILE_BOUNDARY_HEIGHT       (2 * WINDOW_RADIUS)

#define BLOCK_WIDTH                (TILE_SHARED_MEM_WIDTH  - TILE_BOUNDARY_WIDTH) 
#define BLOCK_HEIGHT               (TILE_SHARED_MEM_HEIGHT - TILE_BOUNDARY_HEIGHT)  

#define THREAD_NUM_WIDTH            8
#define THREADS_NUM_HEIGHT         TILE_SHARED_MEM_HEIGHT

 //TODO fix input arguments
__kernel void hello_kernel( __global unsigned char*  d_leftImage,
                            __global unsigned char*  d_rightImage,
                            __global float* d_disparityLeft) {

    int blockX      = get_group_id(0);
    int blockY      = get_group_id(1);
    int threadX     = get_local_id(0);
    int threadY     = get_local_id(1);

    __local unsigned char leftImage      [TILE_SHARED_MEM_WIDTH * TILE_SHARED_MEM_HEIGHT];
    __local unsigned char rightImage     [TILE_SHARED_MEM_WIDTH * TILE_SHARED_MEM_HEIGHT];
    __local unsigned int  partialDiffSum [BLOCK_WIDTH           * TILE_SHARED_MEM_HEIGHT];

    int alignedImageWidth = 640;
    int partialDiffSumTemp;
    float bestDisparity[4] = {0,0,0,0};
    int lowestDist[4];
        lowestDist[0] = 214748364;
        lowestDist[1] = 214748364;
        lowestDist[2] = 214748364;
        lowestDist[3] = 214748364;

    // Read image blocks into shared memory. read is done at 32bit integers on a uchar array. each thread reads 3 integers(12byte) 96/12=8threads
    int sharedMemIdx = threadY * TILE_SHARED_MEM_WIDTH + 4 * threadX; 
    int globalMemIdx = (blockY * BLOCK_HEIGHT + threadY) * alignedImageWidth + blockX * BLOCK_WIDTH + 4 * threadX; 

    for (int i = 0; i < 4; i++) {
        leftImage [sharedMemIdx                        + i ] = d_leftImage [globalMemIdx                        + i];
        leftImage [sharedMemIdx + 4 * THREAD_NUM_WIDTH + i ] = d_leftImage [globalMemIdx + 4 * THREAD_NUM_WIDTH + i];
        leftImage [sharedMemIdx + 8 * THREAD_NUM_WIDTH + i ] = d_leftImage [globalMemIdx + 8 * THREAD_NUM_WIDTH + i];
        rightImage[sharedMemIdx                        + i ] = d_rightImage[globalMemIdx                        + i];
        rightImage[sharedMemIdx + 4 * THREAD_NUM_WIDTH + i ] = d_rightImage[globalMemIdx + 4 * THREAD_NUM_WIDTH + i];
        rightImage[sharedMemIdx + 8 * THREAD_NUM_WIDTH + i ] = d_rightImage[globalMemIdx + 8 * THREAD_NUM_WIDTH + i];
    }

    barrier(CLK_LOCAL_MEM_FENCE);

    int imageIdx = sharedMemIdx + TILE_BOUNDARY_WIDTH - WINDOW_RADIUS;
    int partialSumIdx = threadY * BLOCK_WIDTH + 4 * threadX;

    for(int dispLevel = MIN_DISPARITY; dispLevel <= MAX_DISPARITY; dispLevel++) {

        // horizontal partial sum
        partialDiffSumTemp = 0;
        #pragma unroll
        for(int i = imageIdx - WINDOW_RADIUS; i <= imageIdx + WINDOW_RADIUS; i++) {
                    //partialDiffSumTemp += calcDiff(leftImage [i], rightImage[i - dispLevel]);
                      partialDiffSumTemp += abs(leftImage[i] - rightImage[i - dispLevel]);
        }
        partialDiffSum[partialSumIdx] = partialDiffSumTemp;

        barrier(CLK_LOCAL_MEM_FENCE);

        for (int pixelNum = 1, i = imageIdx - WINDOW_RADIUS; pixelNum < NUM_PIXEL_PER_THREAD; pixelNum++, i++) {
            partialDiffSum[partialSumIdx + pixelNum] = partialDiffSum[partialSumIdx + pixelNum - 1] + 
                                                       abs(leftImage[i + WINDOW_SIZE] - rightImage[i - dispLevel + WINDOW_SIZE]) -
                                                       abs(leftImage[i]               - rightImage[i - dispLevel]);
        }

        barrier(CLK_LOCAL_MEM_FENCE);

        // vertical sum
        if(threadY >= WINDOW_RADIUS && threadY < TILE_SHARED_MEM_HEIGHT - WINDOW_RADIUS) {

            for (int pixelNum = 0; pixelNum < NUM_PIXEL_PER_THREAD; pixelNum++) {
                int rowIdx = partialSumIdx - WINDOW_RADIUS * BLOCK_WIDTH;
                partialDiffSumTemp = 0;

                    for(int i = -WINDOW_RADIUS; i <= WINDOW_RADIUS; i++,rowIdx += BLOCK_WIDTH) {
                           partialDiffSumTemp += partialDiffSum[rowIdx + pixelNum];
                    }

                    if (partialDiffSumTemp < lowestDist[pixelNum]) {
                        lowestDist[pixelNum]    = partialDiffSumTemp;
                        bestDisparity[pixelNum] = dispLevel - 1;
                    }


            }
        }

    }

    if (threadY >= WINDOW_RADIUS && threadY < TILE_SHARED_MEM_HEIGHT - WINDOW_RADIUS && blockY < 32) {

        d_disparityLeft[globalMemIdx + TILE_BOUNDARY_WIDTH - WINDOW_RADIUS + 0] = bestDisparity[0];
        d_disparityLeft[globalMemIdx + TILE_BOUNDARY_WIDTH - WINDOW_RADIUS + 1] = bestDisparity[1];
        d_disparityLeft[globalMemIdx + TILE_BOUNDARY_WIDTH - WINDOW_RADIUS + 2] = bestDisparity[2];
        d_disparityLeft[globalMemIdx + TILE_BOUNDARY_WIDTH - WINDOW_RADIUS + 3] = bestDisparity[3];
    }

}

感谢所有的帮助

尤瓦尔

4

1 回答 1

0

根据我的经验,NVidia GPU 并不总是在越界访问时崩溃,而且很多时候内核仍然返回预期结果。

用于printf检查索引。如果您安装了 Nvidia OpenCL 1.2 驱动程序printf,则应该可以作为核心功能使用。据我检查 Mali-T628 使用 OpenCL 1.1 然后检查是否printf可用作供应商扩展。您也可以在可用的 AMD/Intel CPU 上运行内核printf(OpenCL 1.2 / 2.0)。

检查索引的另一种方法可以是传递__global int* debug存储索引的数组,然后在主机上检查它们。确保分配它足够大,以便记录超出范围的索引。

于 2015-06-07T19:28:30.867 回答