在OpenCL中重叠传输和设备计算

时间:2012-09-12 13:29:41

标签: opencl gpgpu

我是OpenCL的初学者,我很难理解某些东西。 我想改善主机和设备之间的图像传输。 我制定了一个更好地了解我的计划。

顶部:我现在拥有的东西底部:我想要什么 HtD(主机到设备)和DtH(设备到主机)是内存传输。 K1和K2是内核。

我考虑过使用映射内存,但第一次传输(Host to Device)是用clSetKernelArg()命令完成的,不是吗? 或者我是否必须将输入图像剪切为子图像并使用映射来获取输出图像?

感谢。

编辑:更多信息

K1处理mem输入图像。 K2处理输出图像来自K1。

所以,我想将MemInput转移到K1的几个部分。 我想阅读并保存主机上由K2处理的MemOuput。

4 个答案:

答案 0 :(得分:6)

正如您可能已经看到的那样,您使用clEnqueueWriteBuffer和类似的方式从主机转移到设备。

所有具有关键字'排队'的命令在它们中有一个特殊的属性:命令不是直接执行的,但是当你使用clFinishclFlushclEnqueueWaitForEvents,在阻止模式下使用clEnqueueWriteBuffer等等来触发命令时

这意味着所有操作都会立即发生,您必须使用事件对象进行同步。当所有事情(可能)同时发生时,你可以做这样的事情(每个点同时发生):

  1. 转移数据A
  2. 过程数据A&转移数据B
  3. 过程数据B&转移数据C& Retrive Data A'
  4. 过程数据C&检索数据B'
  5. 检索数据C'
  6. 记住:没有事件对象的排队任务可能会导致所有入队元素同时执行!

    为了确保Process B在转移B之前没有发生,您必须从clEnqueueWriteBuffer检索一个事件对象并将其作为一个对象提供给等待f.i. clEnqueueNDRangeKernel

    cl_event evt;
    clEnqueueWriteBuffer(... , bufferB , ... , ... , ... , bufferBdata , NULL , NULL , &evt);
    clEnqueueNDRangeKernel(... , kernelB , ... , ... , ... , ... , 1 , &evt, NULL);
    

    每个命令当然可以等待某些对象并生成一个新的事件对象,而不是提供NULL。最后一个参数是一个数组,所以你可以事件等待几个事件!

    <小时/> 编辑:总结以下评论 传输数据 - 什么命令在哪里工作?

           CPU                        GPU
                                BufA       BufB
    array[] = {...}
    clCreateBuffer()  ----->  [     ]              //Create (empty) Buffer in GPU memory *
    clCreateBuffer()  ----->  [     ]    [     ]   //Create (empty) Buffer in GPU memory *
    clWriteBuffer()   -arr->  [array]    [     ]   //Copy from CPU to GPU
    clCopyBuffer()            [array] -> [array]   //Copy from GPU to GPU
    clReadBuffer()    <-arr-  [array]    [array]   //Copy from GPU to CPU
    

    *您可以通过使用host_ptr参数提供数据来直接初始化缓冲区。

答案 1 :(得分:3)

许多OpenCL平台不支持无序命令队列;大多数供应商说重叠DMA和计算的方式是使用多个(有序)命令队列。您可以使用事件来确保以正确的顺序完成依赖项。 NVIDIA的示例代码显示重叠的DMA和计算以这种方式执行(虽然它不是最理想的;它可以比它们说的更快一些)。

答案 2 :(得分:2)

创建命令队列时,需要在属性中启用无序执行。请参阅:CL_QUEUE_OUT_OF_ORDER_EXEC_MODE_ENABLE,clCreateCommandQueue

这将允许您设置较小的任务链并将它们相互链接。这一切都在主持人身上完成。

主机伪代码:

for i in taskChainList
  enqueueWriteDataFromHost
  enqueueKernel(K1)
  enqueueKernel(K2)
  enqueueReadFromDevice
clfinish

排队任务时,将前一个cl_event放入每个任务的event_wait_list中。我上面的'enqueueWriteDataFromHost'不必等待另一个事件开始。

可替换地,

cl_event prevWriteEvent;
cl_event newWriteEvent;
for i in taskChainList
  enqueueWriteDataFromHost // pass *prevWriteEvent as the event_wait_list, and update with newWriteEvent that the enqueue function produces. Now each Write will wait on the one before it.
  enqueueKernel(K1)
  enqueueKernel(K2)
  enqueueReadFromDevice  //The reads shouldn't come back out of order, but they could (if the last block of processing were much faster then the 2nd-last for example)
clfinish

答案 3 :(得分:2)

正确的方法(就像我一样,并且完美地工作)是创建2个命令队列,一个用于I / O,另一个用于处理。两者都必须在同一个环境中。

您可以使用事件来控制两个队列的计划,并且操作将并行执行(如果可以)。即使设备不支持outoforderqueue,它确实有效。

例如,您可以将I / O队列中的所有100个图像排入GPU并获取其事件。然后将此事件设置为内​​核的触发器。并且DtoH传输由内核事件触发。 即使您将所有这些作业排入AT ONCE,它们也会按顺序处理并使用并行I / O.