clFlush(相对于 clFinish)实际上有什么作用吗?

ein*_*ica 5 opencl

OpenCL clFinish()API 调用会阻塞,直到命令队列上的所有命令完成执行为止。一个相关的函数,clFlush()据说

将命令队列中所有先前排队的 OpenCL 命令发出到与命令队列关联的设备。

这意味着什么?它是否使这些命令跳过等待事件?这听起来不太合理。在发出命令之前它会阻塞吗?可能不是,事实就是如此clFinish()。看起来好像clFlush()实际上不需要做任何事情。

我缺少什么?

Ela*_*oni 5

当您使用 opencl 对异步命令进行排队时,无法真正保证 GPU 会实际执行这些命令。

这些异步命令通常是阻塞标志设置为 CL_FALSE 的内存传输(clEnqueueWriteBuffer、clEnqueueReadBuffer)和内核调用(clEnqueueNDRangeKernel)。

如果您希望保证命令执行,则必须将阻塞命令(例如阻塞标志设置为 CL_TRUE 的 API 调用)排入队列,或者调用 clFinish / clFlush。

clFlush基本上将记录的命令传输到 GPU。这些命令被“刷新”到硬件命令缓冲区,并在 GPU 调度程序安排它们执行后立即执行。

一个重要的极端情况是,记录的命令(或同一队列上先前记录的命令)必须与用户事件 (clCreateUserEvent) 或源自记录到不同队列的命令的事件同步。这些事件通过 OpenCL API 提供,并且可以停止刷新命令,直到触发事件。

为什么clFlush是必要的?

这背后的逻辑是,最有效的方法是让 GPU 充满最大工作量(通过许多排队调用填充大型命令缓冲区),然后告诉 GPU 使用单个异步调用或其阻塞来执行所有clFlush操作变体clFinish

您可能想要调用 clFlush 而不是 clFinish 的原因之一是您希望将CPU 工作与 GPU 工作交错:

clEnqueue*** // async
clEnqueue*** // async
clEnqueue*** // async
clFlush(...); // async, make sure commands will execute
// do some heavy CPU work while GPU is executing commands
clFinish(...); // synchronous, ensure all commands are done, collect results.
Run Code Online (Sandbox Code Playgroud)

一些 OpenCL 实现(例如 AMD)将在clFlush调用之间记录批处理命令。这意味着从事件同步的角度来看,它将把记录的命令视为单个命令。

event1 = clEnqueue*** // async
event2 = clEnqueue*** // async
event3 = clEnqueue*** // async
clFlush(...); // async, previously recorded commands will execute
event4 = clEnqueue*** // async
event5 = clEnqueue*** // async
event6 = clEnqueue*** // async
clFlush(...); // async, previously recorded commands will execute
event3.wait(); 
// Do CPU work while the GPU processes kernels 4-6
event6.wait(); // wait for kernels 4-6
Run Code Online (Sandbox Code Playgroud)

在这种情况下,事件 1-3 将在第 3 个内核完成执行后发出信号,而事件 4-6 将在第 6 个内核完成执行后发出信号。

这允许您在 CPU 端发布处理结果,同时 GPU 仍在工作。

另一个用例clFlush是减少驱动程序工作和 GPU 调度产生的延迟。如果排队的命令必须等待外部事件(屏障),您可以提前将它们刷新到 GPU。一旦事件被触发,命令就已经被刷新到硬件端等待实际执行。从而节省了将记录的命令传输到 GPU 所涉及的驱动程序延迟。