每次执行后在opencl中更新缓冲区

时间:2018-08-12 11:28:55

标签: c opencl

所以我已经编写了一个代码并且可以正常工作,现在我想做的是多次调用clEnqueueNDRangeKernel(),每次执行后我都想用该输出更新缓冲区(缓冲区Y)。我写了下面的代码,我想知道它是否正确。 我没有为此编写单独的setkernelArg()命令。

    for (int a = 0; a < 100; a++)
{
    ret = clEnqueueNDRangeKernel(command_queue, kernel, 1, NULL, globalws, NULL, 0, NULL, NULL);

    if (ret != CL_SUCCESS) {
        printf("Failed to enqueueNDRangeKernel.\n");
        exit(1);
    }

    clEnqueueReadBuffer(command_queue, bufferC, CL_TRUE, 0, M*N * sizeof(float), (void *)C, 0, NULL, NULL);
    clEnqueueWriteBuffer(command_queue, bufferY, CL_TRUE, 0, 1 * N * sizeof(float), (void *)C, 0, NULL, NULL);
    for (int i = 0; i < N; i++) {
        printf("%f, ", C[i]);
        }
}

1 个答案:

答案 0 :(得分:0)

您应该等待每个OpenCL Api调用。为每个呼叫创建事件。因此,您可以确保在开始下一个执行之前,每个执行都已完成。例如,内核有可能在GPU上进行一些计算,但是同时启动clEnqueueReadBuffer并在内核完成向输出缓冲区的写入之前读取输出缓冲区。还有可能在clEnqueueReadBuffer完成之前写入GPU。 OpenCL Api调用开始在GPU上执行,但是主机编程也继续进行。

有了事件,您的程序可能看起来像这样:

cl_event evKernel, evReadBuf, evWriteBuf;

for(int a = 0; a < 100; a++)
{
   ret = clEnqueueNDRangeKernel(command_queue, kernel, 1, NULL, globalws, NULL, 0, NULL, &evKernel);

   if (ret != CL_SUCCESS) {
       printf("Failed to enqueueNDRangeKernel.\n");
       exit(1);
   }
   clWaitForEvents(1, &evKernel);

   clEnqueueReadBuffer(command_queue, bufferC, CL_TRUE, 0, M*N * sizeof(float), (void *)C, 0, NULL, &evReadBuf);
   clWaitForEvents(1, &evReadBuf);
   clEnqueueWriteBuffer(command_queue, bufferY, CL_TRUE, 0, 1 * N * sizeof(float), (void *)C, 0, NULL, &evWriteBuf);
   clWaitForEvents(1, &evWriteBuf);

   for (int i = 0; i < N; i++) {
       printf("%f, ", C[i]);
   }
}

有了事件,循环的执行时间就会增加。

在clEnqueueWriteBuffer调用中,您从主机内存( M*N*sizeof(float) )编写,该内存大于设备缓冲区(1*N*sizeof(float) )。(也许您是指(M*N*sizeof(float))?)主机端的程序会崩溃(无效的内存访问),但是OpenCL不会抱怨它并复制数据。我不确定,但是将来可能会引起问题。

我不知道您的内核会做什么,但是如果内核仅将数据写入主机端所需的Output-Buffer会更好。您将bufferC复制到C,但是仅将C的一部分复制到bufferY,这似乎是下一个内核的输入。也许您可以在内核中进行更改。

将数据从主机复制到设备或将设备复制到主机是昂贵的部分。因此,出于性能方面的考虑,您不应复制不需要进行进一步计算的数据。