【问题标题】:What is the correct way to use queue.flush() and queue.finish() after calling a Kernel?调用内核后使用 queue.flush() 和 queue.finish() 的正确方法是什么?
【发布时间】:2017-06-25 02:32:56
【问题描述】:

我正在为我的项目使用 opencl 1.2 c++ 包装器。我想知道调用内核的正确方法是什么。就我而言,我有 2 台设备,数据应该同时发送给它们。

我将我的数据分成两个块,两个设备应该能够分别对它们执行计算。它们没有互连,也不需要知道其他设备中发生了什么。

当数据被发送到两个设备时,我想在我的程序继续之前等待内核完成。因为我将使用从两个内核返回的结果。所以我不想在内核返回之前开始读取数据。

我有两种方法。就我而言,哪一个在编程上是正确的:

方法一:

for (int i = 0; i < numberOfDevices; i++) {
  // Enqueue the kernel.
  kernelGA(cl::EnqueueArgs(queue[iter],
                         arguments etc...);
  queue[i].flush();
}

// Wait for the kernels to return.
for (int i = 0; i < numberOfDevices; i++) {
  queue[i].finish();
}

方法二:

for (int i = 0; i < numberOfDevices; i++) {
  // Enqueue the kernel.
  kernelGA(cl::EnqueueArgs(queue[iter],
                     arguments etc...);
}

for (int i = 0; i < numberOfDevices; i++) {
  queue[i].flush();
}

// Wait for the kernels to return.
for (int i = 0; i < numberOfDevices; i++) {
  queue[i].finish();
}

或者它们都不正确并且有更好的方法来等待我的内核返回?

【问题讨论】:

  • 方法一是正确的。但是,您可以做得更好。在非阻塞等待中从内核读回数据。然后完成。这样,两个内核都可以在完成后立即开始将数据复制回主机。

标签: c++ kernel opencl


【解决方案1】:

假设每个设备在自己的内存中计算:

我会选择你的方法 1 的多线程 (for) 循环版本。因为 opencl 不强制供应商进行异步入队。例如,Nvidia 对一些驱动程序和硬件进行同步入队,而 amd 有异步入队。

当每个设备由单独的线程驱动时,它们应该在同步读取部分结果之前将写入+计算一起排入队列(第二个线程循环)

拥有多个线程也有利于自旋等待类型同步 (cfinish),因为多个自旋等待循环是并行工作的。这应该以毫秒为单位节省时间。

Flush 帮助一些供应商(如 amd)提前开始排队。

要为所有设备提供正确的输入和正确的输出,只需两个完成命令就足够了。一个在写入+计算之后,然后一个在读取(结果)之后。所以每个设备都获得相同的时间步长数据并在相同的时间步长上产生结果。如果队列类型是有序的,则写入和计算不需要在它们之间完成,因为它会一一计算。这也不需要读取操作被阻塞。

琐碎的完成命令总是会扼杀性能。

注意:我已经使用所有这些编写了一个负载均衡器,当使用基于事件的同步而不是完成时,它的性能更好。完成比基于事件的更容易,但同步时间更长。

此外,单个队列并不总是将 gpu 推到其极限。每个设备至少使用 4 个队列可确保在我的 amd 系统上隐藏写入和计算的延迟。有时甚至 16 个队列也会有所帮助。但是对于io瓶颈的情况可能需要更多。

例子:

 thread1
      Write 
      Compute
      Synchronization with other thread

  Thread2
      Write
      Compute
      Synchronization with other thread




   Thread 1
         Read
         Synchronization with other thread

   Thread2
         Read
         Synchronization with other thread

微不足道的同步会影响性能,因为驱动程序不知道您的意图,他们会保持原样因此您应该消除不必要的完成命令并尽可能将阻塞写入转换为非阻塞写入。

零同步也是错误的,因为opencl不强制厂商在多次入队后开始计算。它可能会在几分钟甚至几秒钟内无限增长到千兆字节的内存。

【讨论】:

  • 对不起,我一定会使用 OpenCL。
  • 或者它可能是不同的opencl进程与ramdrive通信,但纯线程更快。
【解决方案2】:

您应该使用方法 1。clFlush 是保证向设备发出命令(而不仅仅是在发送前缓冲某处)的唯一方法。

【讨论】:

    猜你喜欢
    • 2013-11-14
    • 1970-01-01
    • 1970-01-01
    • 2011-11-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-09-29
    • 1970-01-01
    相关资源
    最近更新 更多