仅对英特尔可见 — GUID: fwh1551278091612
Ixiasoft
仅对英特尔可见 — GUID: fwh1551278091612
Ixiasoft
11.2. 使用硬件内核调用队列
如下图所示,使用调用队列时,系统和OpenCL运行时环境开销(从响应完成到发送下一组调用参数)与内核执行重叠。这样使得内核连续执行,并最大限度地提高系统吞吐量。
当另一个具有相同内核函数名称和程序的列队内核已经在器件上运行时,内核调用在硬件上列队起来,并且满足以下条件:
- OpenCL内核编译未使用禁用硬件内核调用缓冲区(-no-hardware-kernel-invocation-buffer)。
- OpenCL内核编译未使用性能计数器(-profile)
- 列队的OpenCL内核没有printf。
- 命令列队中较早列队的所有事件对象的执行状态都等于CL_COMPLETE。
如果状态为CL_SUBMITTED或者CL_RUNNING,则该状态适用于同一程序中具有相同内核函数名称的已列队内核。
- 事件等待列表中所有事件对象的执行状态等于CL_COMPLETE。
如果状态为CL_SUBMITTED或者CL_RUNNING,则该状态适用于相同器件上同一程序中具有相同内核函数名称的已列队内核。
- 如果OpenCL内核使用异构存储器,则当前在器件上运行的内核和正在列队的内核不会在不同存储器类型上设置相同的存储器对象。
请参考以下两个实例主机代码片段,其中编译器可以将内核调用列入硬件内核调用队列:
实例1
int main() { … clEnqueueNDRangeKernel(queue, kernel, …, NULL); clEnqueueNDRangeKernel(queue, kernel, …, NULL); … }
一旦第一个入列的内核运行,编译器可以将第二个入列的内核列入硬件上。
实例 2
int main() { … clEnqueueNDRangeKernel(queue0, kernel0, …, NULL); clEnqueueNDRangeKernel(queue1, kernel1, …, NULL); clEnqueueNDRangeKernel(queue0, kernel0, …, NULL); clEnqueueNDRangeKernel(queue1, kernel1, …, NULL); … }
一旦kernel0的第一个enqueue运行,编译器就可将kernel0的第二个enqueue列入硬件,无论kernel1状态如何。同样地,一旦kernel1的第一个enqueue运行,编译器就可将kernel1的第二个enqueue列入硬件,无论kernel0状态如何。
现在,请参考编译器无法将内核调用列入硬件的两个实例,如下:
实例1
int main() { … clEnqueueNDRangeKernel(queue, kernel0, …, NULL); clEnqueueNDRangeKernel(queue, kernel1, …, NULL); clEnqueueNDRangeKernel(queue, kernel0, …, NULL); … }
由于队列是有序的,enqueue(入列)kernel1会阻止kernel0的第二个enqueue在硬件调用上排队。
实例 2
int main() { … clEnqueueNDRangeKernel(queue0, kernel0, …, NULL); clEnqueueNDRangeKernel(queue1, kernel1, …, &event); clFlush(queue1); clEnqueueNDRangeKernel(queue0, kernel0, …, 1, &event, NULL); … }
因为kernel0的第二个enqueue正在等待kernel1上的enqueue完成,如果kernel1在kernel0的第一个enqueue完成之前先完成执行,它只会在硬件内核调用队列中排队。