8

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

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

我考虑过使用映射内存,但第一次传输(主机到设备)是使用 clSetKernelArg() 命令完成的,不是吗?还是我必须将输入图像切割成子图像并使用映射来获取输出图像?

谢谢。

编辑:更多信息

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

因此,我想将 MemInput 转换为 K1 的多个部分。我想在主机上读取并保存 K2 处理的 MemOuput。

4

4 回答 4

7

正如您可能已经看到的那样,您可以通过使用clEnqueueWriteBuffer和类似的方式从主机传输到设备。

所有带有关键字 'enqueue' 的命令都有一个特殊的属性:这些命令不是直接执行的,而是当你使用clFinishclFlushclEnqueueWaitForEventsclEnqueueWriteBuffer在阻塞模式下使用等等来触发它们时。

这意味着所有动作同时发生,您必须使用事件对象同步它。由于一切(可能)同时发生,您可以执行以下操作(每个点同时发生):

  1. 传输数据 A
  2. 处理数据 A 和传输数据 B
  3. 处理数据 B & 传输数据 C & 检索数据 A'
  4. 处理数据 C & 检索数据 B'
  5. 检索数据 C'

请记住:没有事件对象的入队任务可能会导致所有入队元素的同时执行!

为确保流程数据 B 不会在传输 B 之前发生,您必须从中检索事件对象clEnqueueWriteBuffer并将其作为对象提供以等待 ficlEnqueueNDRangeKernel

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

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


编辑:总结下面的评论 传输数据-什么命令在哪里起作用?

       CPU GPU 
                            BufA        BufB
数组[] = {...}
clCreateBuffer() ----->   [ ]               //在GPU内存中创建(空)Buffer *
clCreateBuffer() -----> [ ]     [ ]    //在GPU内存中创建(空)Buffer *
clWriteBuffer() -arr-> [ array ] [ ] //从CPU复制到GPU
clCopyBuffer() [array] -> [ array ] //从GPU复制到GPU
clReadBuffer() <-arr- [array] [array] //从GPU复制到CPU

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

于 2012-09-12T13:48:58.833 回答
3

许多 OpenCL 平台不支持乱序命令队列;大多数供应商所说的重叠 DMA 和计算的方式是使用多个(按顺序)命令队列。您可以使用事件来确保以正确的顺序完成依赖关系。NVIDIA 有示例代码显示重叠的 DMA 并以这种方式进行计算(尽管它不是最理想的;它可能比他们说的要快一些)。

于 2012-10-15T00:13:02.503 回答
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
于 2012-09-12T18:45:39.100 回答
2

正确的方法(正如我所做的并且确实完美地工作)是创建 2 个命令队列,一个用于 I/O,另一个用于处理。两者必须在相同的上下文中。

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

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

于 2013-01-16T23:02:42.523 回答