0

我有很长的内核序列,我需要在一些数据上运行,比如

data -> kernel1 -> data1 -> kernel2 -> data2 -> kernel3 -> data3 etc.

我还需要将所有中间结果复制回主机,所以这个想法类似于(伪代码):

inputdata = clCreateBuffer(...hostBuffer[0]);

for (int i = 0; i < N; ++i)
{
    // create output buffer
    outputdata = clCreateBuffer(...);

    // run kernel
    kernel = clCreateKernel(...);
    kernel.setArg(0, inputdata);
    kernel.setArg(1, outputdata);
    enqueueNDRangeKernel(kernel);

    // read intermediate result
    enqueueReadBuffer(outputdata, hostBuffer[i]);

    // output of operation becomes input of next
    inputdata = outputdata;
}

有几种方法可以安排这些操作:

  • 最简单的是总是等待上一个入队操作的事件,所以我们在继续下一个内核之前等待读取操作完成。我可以在不需要缓冲区时立即释放它们。
  • 或者使一切尽可能异步,其中内核和读取队列只等待以前的内核,因此缓冲区读取可以在另一个内核运行时发生。

在第二种(异步)情况下,我有几个问题:

  • 我是否必须在长链操作中保留对所有 cl_mem 对象的引用并在一切完成后释放它们?
  • 重要的是,当所有内存对象的总和超过设备上可用的总内存时,OpenCL 如何处理这种情况?在任何时候内核都只需要输入和输出内核(应该适合内存),但是如果这些缓冲区中有 4 或 5 个超过了总数,OpenCL 如何在幕后分配/释放这些内存对象?这对读取有何影响?

如果有人能澄清在这些情况下会发生什么,我将不胜感激,也许在 OpenCL 规范中有与此相关的内容。

谢谢你。

4

2 回答 2

1

你的第二种情况是要走的路。

在第二种(异步)情况下,我有几个问题:

Do I have to keep references to all cl_mem objects 
in the long chain of actions and release them after 
everything is complete?

是的。但是,如果所有数据数组的大小相同,我将只使用 2 个,并且每次迭代一个接一个地覆盖。那么你只需要有 2 个内存区域,并且释放和分配应该只发生在开始/结束。

不要担心数据有错误的值,如果您设置正确的事件,处理将等待 I/O 完成。IE:

data -> kernel1 -> data1 -> kernel2 -> data -> kernel3 -> data1
                -> I/O operation    -> I/O operation

为此,只需设置一个条件,强制 kernel3 仅在第一个 I/O 完成时启动。您可以通过这种方式链接所有事件。

注意:使用 2 个队列,一个用于 I/O,另一个用于处理将带来并行 I/O,速度提高 2 倍。

重要的是,当所有内存对象的总和超过设备上可用的总内存时,OpenCL 如何处理这种情况?

分配时给出错误 OUT_OF_RESOURCES 或类似错误。

在任何时候内核都只需要输入和输出内核(应该适合内存),但是如果这些缓冲区中有 4 或 5 个超过了总数,OpenCL 如何在幕后分配/释放这些内存对象?这对读取有何影响?

它不会自动执行此操作,除非您已将内存设置为主机 PTR。但我不确定 OpenCL 驱动程序是否能正确处理它。如果我是你,我不会分配超过最大值。

于 2013-01-16T23:34:35.457 回答
1

我的印象是(对不起,我打算引用规范但今天找不到,所以我降低了我的断言的强度)当你用 cl_mem 引用将内核排入队列时,它需要保留这些对象,并且内核完成后释放它们。

这可以让您在将内核加入队列后释放这些对象,而无需等待内核完成运行。这就是异步“clEnqueue”操作与同步操作(即内存释放)协调的方式,并防止运行时和内核使用释放的内存对象。

于 2013-01-19T19:37:06.083 回答