Профилирование AMD Codexl: обнаружена утечка памяти Opencl [Ссылка = 1] Объект, созданный clEnqueueNDRangeKernel

Я понятия не имею, почему я продолжаю получать это предупреждение, поскольку я много раз просматривал код, и я не могу понять, откуда это берется, поскольку я почти уверен (но явно не) освобождаю всю память. Надеюсь, кто-то, кто знает больше меня, сможет взглянуть на мой код и указать, где и почему это происходит.

Спасибо!

int runKernel( Image *anImage, 
              PixelPacket *imagePixels, 
              MagickSizeType imageSizeBytes, 
              const char *kernelSource )
{

    cl_context myContext ;
    cl_command_queue myQueue ;
    cl_mem *outputImage ;
    cl_event clEvent ;
    int bitsPerChannel = anImage[0].depth ; 
    int width = anImage[0].columns ;
    int height = anImage[0].rows ;



    /****************************
    Setup the Opencl environment
    ****************************/


    // Use this to check the output of each API call
    cl_int status ;

    // Retrieve the number of platforms
    cl_uint numPlatforms = 0 ;
    status = clGetPlatformIDs( 0, NULL, &numPlatforms ) ;

    // Allocate enough space for each platform
    cl_platform_id *platforms = NULL ;
    platforms = (cl_platform_id *) malloc( numPlatforms * sizeof(cl_platform_id) ) ;

    // Fill in the platforms
    status = clGetPlatformIDs( numPlatforms, platforms, NULL ) ;

    // Retrieve the number of devices for the 1st platform
    cl_uint numDevices = 0 ;
    status = clGetDeviceIDs( platforms[0], CL_DEVICE_TYPE_ALL, 0, NULL, &numDevices ) ;

    // Allocate enough space for each device
    cl_device_id *devices ;
    devices = (cl_device_id *) malloc( numDevices * sizeof(cl_device_id) ) ;

    // Fill in the devices
    status = clGetDeviceIDs( platforms[0], CL_DEVICE_TYPE_ALL, numDevices, 
                devices, NULL ) ;

    // Create the context
    myContext = clCreateContext( NULL, numDevices, devices, NULL, NULL, &status ) ;

    // Create the command queue with the 1st device
    myQueue = clCreateCommandQueue( myContext, devices[0], 0, &status ) ;



    /****************************
    Create Images and Move Data
    ****************************/



    // Set format and descriptor to proper values according to image type
    cl_image_format *image_format = NULL ;
    cl_image_desc *image_desc = NULL ;
    get_cl_image_format( anImage, &image_desc, &image_format ) ;

    // Create the image sampler
    cl_sampler clSampler = clCreateSampler(
                            myContext,
                            CL_FALSE, //use pixel based addressing not normalized
                            CL_ADDRESS_CLAMP_TO_EDGE, // set equal to the pixel at the edge of the image
                            CL_FILTER_NEAREST, 
                            &status);

    // Set input Image region parameters
    size_t origin[3] = {0, 0, 0} ; // Offset within the image to copy from
    size_t region[3] = {width, height, 1} ; // Elements per dimension for 2d image 


    // Create cl memory object for the input image
    cl_mem_flags flagsRead = CL_MEM_READ_ONLY | CL_MEM_USE_HOST_PTR ;

    cl_mem clInput = clCreateImage( myContext, flagsRead, 
                                ( const cl_image_format *)image_format, 
                                ( const cl_image_desc *)image_desc, 
                                imagePixels, 
                                &status ) ;


    // Allocate space for output image and create cl memory object 
    float *outputPixels =  (float *) malloc( imageSizeBytes ) ;
    cl_mem_flags flagsWrite = CL_MEM_WRITE_ONLY | CL_MEM_USE_HOST_PTR ;

    cl_mem clOutput = clCreateImage( myContext, flagsWrite, 
                                    (const cl_image_format *)image_format, 
                                    ( const cl_image_desc *)image_desc, 
                                    outputPixels, 
                                    &status ) ;


    //Copy input image to the device
    status = clEnqueueWriteImage( myQueue, clInput, CL_FALSE, origin, region,
                                0, 0, imagePixels, 0, NULL, NULL ) ;



    /*****************************
    Compile the kernel from source
    *****************************/



    // kernelSource stores the kernel code and must be NULL terminated
    cl_program myProgram = clCreateProgramWithSource( myContext, 1, 
                                                    &kernelSource, 
                                                    NULL, 
                                                    &status ) ;

    // Compile the program  
    const char buildOptions[] = "-cl-std=CL1.2 -cl-mad-enable\0";
    status = clBuildProgram( myProgram, 1, devices, buildOptions, NULL, NULL ) ;

    // Create the kernel
    cl_kernel myKernel = clCreateKernel( myProgram, "convolution", &status ) ;



    /**********************************
    Set kernel args and run the program
    **********************************/



    // Set the kernel arguments

    clSetKernelArg( myKernel, 0, sizeof( cl_mem ), &clInput ) ;
    clSetKernelArg( myKernel, 1, sizeof( cl_mem ), &clOutput ) ;
    clSetKernelArg( myKernel, 2, sizeof( int ), &height ) ;
    clSetKernelArg( myKernel, 3, sizeof( int ), &width ) ;
    clSetKernelArg( myKernel, 4, sizeof( cl_sampler ), &clSampler ) ;

    //Execute the kernel
    status = clEnqueueTask( myQueue, myKernel, 0, NULL, NULL ) ;

    //Read the output buffer back to the host
    status = clEnqueueReadImage( myQueue, clOutput, CL_TRUE, origin, region, 0, 0, 
                               (void *) outputPixels, 0, NULL, &clEvent ) ;



    /**********************************
    Free Resources
    **********************************/



    /* Wait for the kernel to finish */ 
    clWaitForEvents( 1, &clEvent ) ;

    free( refImage ) ;
    free( platforms ) ;
    free( devices ) ;
    free( outputPixels ) ;
    free( image_desc ) ;
    free( image_format ) ;

    clReleaseSampler( clSampler ) ;
    clReleaseMemObject( clInput ) ;
    clReleaseMemObject( clOutput ) ;    
    clReleaseProgram( myProgram ) ;
    clReleaseCommandQueue( myQueue ) ;
    clReleaseKernel( myKernel ) ;   
    clReleaseContext( myContext ) ; 
    clReleaseEvent( clEvent ) ;

    return 0;
}

person Jason White    schedule 14.04.2014    source источник
comment
Ваше сообщение об ошибке указывает на то, что утечка памяти происходит в clEnqueueNDRangeKernel. Если я не слепой, самое близкое, что вы получите, это clEnqueueTask, который может вызывать clEnqueueNDRangeKernel. После внесения исправления, предложенного tim, закомментируйте строку clEnqueueTask и посмотрите, сохраняется ли утечка памяти. Если его нет, то я подозреваю, что объект cl_event создается в clEnqueueTask, даже если вы передаете NULL для возвращаемого события.   -  person chippies    schedule 15.04.2014
comment
Эй, Чиппи, кажется, ты прав! Я добавил clEvent вместо NULL и сразу же выпустил его, и это устраняет утечку. Спасибо!   -  person Jason White    schedule 16.04.2014
comment
Интересно, это ошибка в драйвере? То, что вы сделали, было совершенно правильно. Это был clEnqueueTask, а не вы.   -  person Tim    schedule 16.04.2014
comment
@ Тим, я согласен. Насколько я понимаю спецификации, все функции не должны создавать событие при передаче NULL для параметра события. Для дальнейшего использования OP разместил это на форумах разработчиков AMD по адресу http://devgurus.amd.com/thread/168582.   -  person chippies    schedule 17.04.2014


Ответы (1)


Возможно, предупреждение вызывается уничтожением очереди, ядра, контекста или программы перед выпуском события (clEvent). Вы можете попробовать следующее:

clReleaseEvent( clEvent ) ; // <<< THIS ONE FIRST
clReleaseSampler( clSampler ) ;
clReleaseMemObject( clInput ) ;
clReleaseMemObject( clOutput ) ;    
clReleaseProgram( myProgram ) ;
clReleaseCommandQueue( myQueue ) ;
clReleaseKernel( myKernel ) ;   
clReleaseContext( myContext ) ; 

В качестве альтернативы отлаживайте операции выпуска построчно, пока не получите предупреждение.

person Tim    schedule 15.04.2014
comment
Эй Тим, я пробовал это, но это не имело никакого эффекта. Порядок, похоже, не имеет значения, так как я попробовал это в конце один раз, и он все еще показывает то же самое, но спасибо за попытку! - person Jason White; 16.04.2014
comment
Я не уверен, что говорит спецификация CL, но в целом было бы неплохо быть чувствительным к порядку уничтожения, если вы запускаете это в других реализациях. Престижность реализации AMD за разрешение любого порядка уничтожения! - person Tim; 16.04.2014
comment
Функции clRelease* уменьшают значение счетчика ссылок. Как только этот счетчик достигает 0 и все объекты, прикрепленные к освобождаемому объекту, удаляются, объект удаляется. Правильно написанная реализация должна затем внутренне отсортировать порядок, в котором объекты удаляются, независимо от порядка, в котором вы их освобождаете, с тем ограничением, что вы должны освободить их, прежде чем их можно будет удалить. - person chippies; 17.04.2014