当你使用opencl排队异步命令时,不能保证GPU实际执行这些命令。这些异步命令通常是设置为CL_FALSE的内存传输(clEnqueueWriteBuffer、clEnqueueReadBuffer)和内核调用(clEnqueueNDRangeKernel)。
如果你希望保证命令会执行,必须排队一个阻塞命令,例如将阻塞标志设置为CL_TRUE的API调用,或调用clFinish/clFlush。
clFlush基本上是将记录的命令传输到GPU。命令被“刷新”到硬件命令缓冲区,并在GPU调度程序安排它们执行时执行。
一个重要的特例是,当记录的命令(或先前在同一队列上记录的命令)必须与用户事件(clCreateUserEvent)或源自于记录到不同队列的命令的事件进行同步时,这些事件是通过OpenCL API提供的,并且可以阻塞刷新的命令,直到触发事件。
为什么需要clFlush?
背后的逻辑是,最有效的方法是通过许多排队调用来填充一个大的命令缓冲区,然后使用单个异步调用clFlush或其阻塞变体clFinish告诉GPU执行所有内容。
你可能想调用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.
一些 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
在这种情况下,第1至3个事件将在第三个内核执行完成后被标记,而第4至6个事件将在第六个内核执行完成后被标记。
这使您可以在GPU仍在工作时在CPU端进行结果的后处理。
另一个使用
clFlush
的用例是减少来自驱动程序工作和GPU调度的延迟。如果您的排队命令必须等待外部事件(障碍),您可以事先刷新它们到GPU。一旦触发了事件,命令已经被刷新到等待实际执行的硬件侧。从而节省了传输记录的命令到GPU所涉及的驱动程序延迟。
clFlush
可用于立即将当前队列提交到设备。请参见此链接。 - doqtorclFinish
不同,clFlush
是异步的,这意味着您立即获得控制权,并且在GPU计算某些内容时可以同时执行其他操作。 - doqtorclFlush
/clFinish
开始执行clEnqueueNDRangeKernel
,它是一种懒加载方式(只有被添加到队列中才会执行)- 只有clFlush
以非阻塞的方式执行。 - doqtor