【发布时间】:2019-05-26 14:24:20
【问题描述】:
我正在运行一个 OpenCL 内核,它一遍又一遍地处理和重新处理相同的数据集(它是一个迭代物理求解器)。
在我的测试中,调用 clEnqueueNDRangeKernel 需要付出不小的代价。例如,当运行模拟的 1000 个子步骤时(需要对 clEnqueueNDRangeKernel 进行 1000 次相同的调用来处理相同的数据),这些对 clEnqueueNDRangeKernel 的调用似乎实际上成为了瓶颈。我的(伪)代码如下所示:
[create buffers]
[set kernel arguments]
for (int i = 0; i < 1000; i++) //queuing the kernels takes a while
{
clEnqueueNDRangeKernel(queue, kernel, args...);
}
clFinish(queue); //waiting for the queue to complete doesn't take much time
[read buffers]
我知道第一次调用 clEnqeueuNDRangeKernel 将初始化任何延迟到 GPU 的缓冲区传输......所以第一次调用可能会产生额外的费用。然而,在我的测试中,10 次迭代的循环比 1000 次迭代要快得多,这让我相信数据传输不是瓶颈。
我还认为 clEnqueueNDRangeKernel 是非阻塞的,因为它在内核完成之前不会阻塞,因此内核的复杂性不应该是瓶颈(在我的情况下,内核在调用 clFinish()) 之前,执行不应阻塞。
但是,当我分析我的代码时,大部分时间都花在处理 for 循环上,在调用 clFinish() 之前......所以看起来内核本身的排队是花费最多时间的在这里。
我的问题:有没有办法告诉 GPU 重新运行先前排队的内核 N 次,而不必手动将内核排队 N 次?在我的情况下,每次迭代都不需要更改或更新内核的参数……只需要重新运行内核即可。重复调用它可以提高效率吗?
【问题讨论】:
-
您是否尝试过每说 100 次迭代调用
clFlush()来触发内核处理? -
@doqtor 刚才试过了...没有明显的速度增加,并且 flush 命令显然有它自己的开销,实际上进一步减慢了速度...
-
由于您没有传递任何新数据或缓冲区,您是否尝试过在 1 个内核中循环处理而不是启动多个内核?也正如 doqtor 所说,尝试发布 100 个实例。但是这次每 50 个实例放置一个事件,然后不要将任何进一步的内核排入队列。当第 50 个实例完成时,再入队一百个,依此类推。这有助于避免过度填充缓冲区
-
@gallickgunner “在一个循环中在 1 个内核中进行处理”是什么意思?这样做我不会失去并行性吗?我正在处理数百万个元素,因此我需要最大化线程使用率。我尝试每隔几百次调用调用一次 clFinish(而不是如上所述的 clFlush),但再次没有获得性能提升。
-
我在一个循环中处理 1 个内核的意思是你只是在一个 for 循环或其他东西中进行处理。您也不会以这种方式获得任何并行性。 GPU 一次可以处理 1 个内核,其他的只是排队。分析 1 个内核在循环中进行处理所花费的时间以及您的方法,即,而不是多次循环入队。然后看看哪个更好
标签: c++ performance queue kernel opencl