OpenCL,使用两个命令队列为单个设备进行双缓冲

时间:2017-03-16 14:34:51

标签: opencl

我正在使用openCL 1.2创建一个应用程序,这是对更大应用程序的测试。对于每个内核执行,该测试将4x4矩阵的每个值加1。这个想法是让双缓冲工作。我创建了两个实际上相同的内核,它们共享相同的READ_WRITE缓冲区,因此每个内核执行都可以在最后一个内存执行的地方继续执行,但它们之间的区别是因为它们有不同的输出缓冲区,允许使用其中一个输出缓冲区一个内核在读取另一个的数据时,就像这样:

Diagram

我认为相关或可能有问题的代码片段如下,我包括队列,缓冲区和事件以防万一,但我尝试改变一切关于此:

队列

compute_queue = clCreateCommandQueueWithProperties(context, device_id, 0, &err);
data_queue = clCreateCommandQueueWithProperties(context, device_id, 0, &err);

缓冲器

input_Parametros = clCreateBuffer(context, CL_MEM_READ_WRITE | CL_MEM_COPY_HOST_PTR, sizeof(double) * 5, Parametros, NULL);
input_matA = clCreateBuffer(context, CL_MEM_READ_WRITE | CL_MEM_COPY_HOST_PTR, sizeof(double) * 4, matA_1, NULL); // The 4x4 matrix
output_buffer = clCreateBuffer(context, CL_MEM_WRITE_ONLY , sizeof(double) * 4 * iteraciones_por_kernel, NULL, NULL);
output_buffer_2 = clCreateBuffer(context, CL_MEM_WRITE_ONLY , sizeof(double) * 4 * iteraciones_por_kernel, NULL, NULL);

为每个内核设置的参数

    clSetKernelArg(kernel_1, 0, sizeof(cl_mem), &input_matA);
    clSetKernelArg(kernel_1, 1, sizeof(cl_mem), &input_Parametros);
    clSetKernelArg(kernel_1, 3, sizeof(cl_mem), &output_buffer);

    clSetKernelArg(kernel_2, 0, sizeof(cl_mem), &input_matA);
    clSetKernelArg(kernel_2, 1, sizeof(cl_mem), &input_Parametros);
    clSetKernelArg(kernel_2, 3, sizeof(cl_mem), &output_buffer_2);

事件

cl_event event_1, event_2, event_3, event_4;

内核和读入队列

    ////////////////////////////////////////////////////////////////
    // START
    ////////////////////////////////////////////////////////////////

clEnqueueNDRangeKernel(compute_queue, kernel_1, 1, NULL, global, local, 0, 0, &event_1);

clEnqueueNDRangeKernel(compute_queue, kernel_2, 1, NULL, global, local, 0, 0, &event_2);

clEnqueueReadBuffer(data_queue, output_buffer, CL_FALSE, 0, sizeof(double)*4*iteraciones_por_kernel, datos_salida, 1 , &event_1, &event_3);

    ////////////////////////////////////////////////////////////////
    // ENQUEUE LOOP
    ////////////////////////////////////////////////////////////////

for (int i = 1; i <= (n_iteraciones_int - 2); i++){

        ////////////////////////////////////////////////////////////////
        // LOOP PART 1
        ////////////////////////////////////////////////////////////////

        if (i % 2 != 0){
            clEnqueueNDRangeKernel(compute_queue, kernel_1, 1, NULL, global, local, 1, &event_3, &event_1);

            clEnqueueReadBuffer(data_queue, output_buffer_2, CL_FALSE, 0, sizeof(double) * 4 * iteraciones_por_kernel, &datos_salida[i*iteraciones_por_kernel_int*4], 1, &event_2, &event_4);
         }

        ////////////////////////////////////////////////////////////////
        // LOOP PART 2
        ////////////////////////////////////////////////////////////////

        if (i % 2 == 0){

            clEnqueueNDRangeKernel(compute_queue, kernel_2, 1, NULL, global, local, 1, &event_4, &event_2);

            clEnqueueReadBuffer(data_queue, output_buffer, CL_FALSE, 0, sizeof(double) * 4 * iteraciones_por_kernel, &datos_salida[i*iteraciones_por_kernel_int * 4], 1, &event_1, &event_3);
        }

    }

    ////////////////////////////////////////////////////////////////
    // END
    ////////////////////////////////////////////////////////////////

clEnqueueReadBuffer(data_queue, output_buffer_2, CL_TRUE, 0, sizeof(double) * 4 * iteraciones_por_kernel, &datos_salida[(n_iteraciones_int - 1) * 4], 1, &event_2, 0);

即使一切看似完美无缺,我也无法让它工作。第一次读取给出了期望值,但从那时起它就像内核不再执行一样,因为我从output_buffer_2得到0,并且第一次读取的值与第一个output_buffer相同。

这对于相同的内核完全正常,只有一个队列可以在最后完成一次数据传输,但我不希望这样。

我修改了所有内容并尽可能多地进行调查,尝试了我能想象到的每一个变化。这应该是容易的,我认为......问题在哪里?

如果有任何帮助,我将使用AMD HD7970作为设备,Windows 10和Visual Studio社区2013。

1 个答案:

答案 0 :(得分:1)

感谢huseyin tugrul buyukisik的帮助,该计划使用了以下变体:

<强>事件

cl_event event[20]; //adjust this to your needs

内核和读入队列

////////////////////////////////////////////////////////////////
// START
////////////////////////////////////////////////////////////////

clEnqueueNDRangeKernel(compute_queue, kernel_1, 1, NULL, global, local, 0, 0, &event[0]);


clEnqueueNDRangeKernel(compute_queue, kernel_2, 1, NULL, global, local, 0, 0, &event[1]);


clEnqueueReadBuffer(data_queue, output_buffer, CL_FALSE, 0, sizeof(double)*4*iteraciones_por_kernel, datos_salida, 1 , &event[0], &event[2]);

////////////////////////////////////////////////////////////////
// LOOP
////////////////////////////////////////////////////////////////

for (int i = 1; i <= (n_iteraciones_int - 2); i++){

        ////////////////////////////////////////////////////////////////
        // LOOP PART 1
        ////////////////////////////////////////////////////////////////

        if (i % 2 == 1){

            clEnqueueNDRangeKernel(compute_queue, kernel_1, 1, NULL, global, local, 1, &event[2+2*(i - 1)], &event[4 + 2 * (i - 1)]); 

            clEnqueueReadBuffer(data_queue, output_buffer_2, CL_FALSE, 0, sizeof(double) * 4 * iteraciones_por_kernel, &datos_salida[i*(iteraciones_por_kernel_int) * 4], 1, &event[1+2*(i - 1)], &event[3 + 2 * (i - 1)]);

        }

        ////////////////////////////////////////////////////////////////
        // LOOP PART 2
        ////////////////////////////////////////////////////////////////

        if (i % 2 == 0){

            clEnqueueNDRangeKernel(compute_queue, kernel_2, 1, NULL, global, local, 1, &event[3 + 2 * (i - 2)], &event[5 + 2 * (i - 2)]);

            clEnqueueReadBuffer(data_queue, output_buffer, CL_FALSE, 0, sizeof(double) * 4 * iteraciones_por_kernel, &datos_salida[i*(iteraciones_por_kernel_int) * 4], 1, &event[4 + 2 * (i - 2)], &event[6 + 2 * (i - 2)]);
        }

    }

////////////////////////////////////////////////////////////////
// END
////////////////////////////////////////////////////////////////

clFlush(compute_queue);
clFlush(data_queue);
clEnqueueReadBuffer(data_queue, output_buffer_2, CL_TRUE, 0, sizeof(double) * 4 * iteraciones_por_kernel, &datos_salida[(n_iteraciones_int-1)*(iteraciones_por_kernel_int) * 4], 1, &event[5+2*(n_iteraciones_int-4)], 0);