【问题标题】:Does clFlush (as opposed to clFinish) actually do anything?clFlush(与 clFinish 相对)实际上有什么作用吗?
【发布时间】:2022-03-18 18:38:01
【问题描述】:

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

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

这是什么意思?它是否使这些命令跳过等待事件?这听起来不合理。在发出命令之前它会阻塞吗?可能不是,这就是clFinish() 所做的。似乎clFlush() 实际上不需要做任何事情。

我错过了什么?

【问题讨论】:

  • 在主机端排队大量内核调用可能会消耗大量 RAM。 clFlush 可用于立即将当前队列提交到设备。见this
  • @doqtor: 但是如果这个提交是可能的,为什么还没有发生呢?即向设备提交队列命令并等待有什么好处?此外 - 如果有人将数万或数十万个内核排入队列 - 那么我 100% 确定他们使用 OpenCL 是错误的(比如链接上的 OP,他应该使用一个内核进行一个副本上的所有扫描,或者一个内核用于扫描所有副本)。
  • clFinish 不同,clFlush 是异步的,这意味着您可以立即获得控制权,并且您可以在 GPU 计算某些内容时同时执行其他操作。
  • @doqtor:我意识到它是异步的,我只是不明白为什么 clFlush 可以做的任何事情在它被调用之前都不应该发生。
  • clFlush/clFinish 开始执行 clEnqueueNDRangeKernel 这是惰性的(仅添加到队列中) - 只是 clFlush 以非阻塞方式执行。

标签: opencl


【解决方案1】:

当您使用 opencl 将 异步 命令排入队列时,并不能真正保证 GPU 会真正执行这些命令。

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

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

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

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

为什么需要clFlush

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

您可能希望通过 clFinish 调用 clFlush 的一个原因是,如果您希望 交错 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 所涉及的驱动程序延迟。

【讨论】:

  • 因此,从“软件缓冲区”移动到“硬件缓冲区”。但是 - 无论如何,这不会自动发生吗?例如,在 CUDA 中 - 没有这样的“刷新”操作。此外,如果硬件“命令缓冲区”不够大,那么 clFlush 甚至无法保证它应该保证的内容。或者 - 也许它会阻塞?
  • 具体行为取决于实现。建议使用 codexl / nsight 等工具自行检查。根据我的经验, clflush 即使对单个内核也有影响。我不记得 cuda 是否有这个功能,但是其他的 api,比如 directx 和 opengl 肯定有。
  • @einpoklum 看看这里的备注部分docs.microsoft.com/en-us/windows/win32/api/d3d12/…。这个想法是一样的。
  • 那个链接描述的东西听起来更像clFinish()
猜你喜欢
  • 1970-01-01
  • 2012-08-20
  • 1970-01-01
  • 2014-10-08
  • 1970-01-01
  • 2016-11-09
  • 2014-03-26
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多