【问题标题】:OpenCL, double buffering using two command-queues for a single deviceOpenCL,使用单个设备的两个命令队列进行双缓冲
【发布时间】:2017-08-07 19:17:19
【问题描述】:

我正在使用 openCL 1.2 创建一个应用程序,这是对更大应用程序的测试。该测试在每次内核执行时将 1 与 4x4 矩阵的每个值相加。这个想法是让双缓冲发挥作用。我创建了两个实际上做同样事情的内核,它们共享相同的 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 Community 2013。

【问题讨论】:

  • 两个模数检查都是针对零的。一个应该是 1
  • 最新的阻塞读取需要一个事件。确保它在返回该事件的 Last 迭代中具有正确的步骤。您忘记与 1 进行比较的奇数最后一次迭代
  • 最后,您可能需要在循环后对两个队列进行 clflush 以启动两个队列,而最后一次读取仅启动它自己的队列 iirc
  • 并且为每次迭代创建一对新的事件不会有什么坏处。由于第一次迭代的完成可能使所有后续迭代立即开始而无需等待使用相同(已完成)事件时
  • 非常感谢!这很有意义,现在 porgram 可以正常工作了!我在任何地方都找不到一个明确的例子,所以我会发布代码以防它对任何人有任何帮助。我认为为每次迭代创建事件是关键。

标签: opencl


【解决方案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);

【讨论】:

    猜你喜欢
    • 2015-07-14
    • 1970-01-01
    • 2012-10-06
    • 2019-07-29
    • 2020-09-18
    • 2017-07-29
    • 1970-01-01
    • 2011-02-20
    • 2010-10-09
    相关资源
    最近更新 更多