我正在编写一个与我的AMD Radeon HD 7800系列GPU一起使用的OpenCL程序。根据AMD的OpenCL编程指南,这一代GPU具有两个可以异步操作的硬件队列。
5.5.6命令队列
对于Southern Islands及更高版本,设备至少支持两个硬件计算队列。这样一来,应用程序就可以通过两个用于异步提交和可能执行的命令队列来提高小型调度的吞吐量。硬件计算队列按以下顺序选择:第一个队列=偶数OCL命令队列,第二个队列=奇数OCL队列。
为此,我创建了两个单独的OpenCL命令队列,以将数据提供给GPU。大致来说,在主机线程上运行的程序如下所示:
static const int kNumQueues = 2;
cl_command_queue default_queue;
cl_command_queue work_queue[kNumQueues];
static const int N = 256;
cl_mem gl_buffers[N];
cl_event finish_events[N];
clEnqueueAcquireGLObjects(default_queue, gl_buffers, N);
int queue_idx = 0;
for (int i = 0; i < N; ++i) {
cl_command_queue queue = work_queue[queue_idx];
cl_mem src = clCreateBuffer(CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR, ...);
// Enqueue a few kernels
cl_mem tmp1 = clCreateBuffer(CL_READ_WRITE);
clEnqueueNDRangeKernel(kernel1, queue, src, tmp1);
clEnqueueNDRangeKernel(kernel2, queue, tmp1, tmp1);
cl_mem tmp2 = clCreateBuffer(CL_READ_WRITE);
clEnqueueNDRangeKernel(kernel2, queue, tmp1, tmp2);
clEnqueueNDRangeKernel(kernel3, queue, tmp2, gl_buffer[i], finish_events + i);
queue_idx = (queue_idx + 1) % kNumQueues;
}
clEnqueueReleaseGLObjects(default_queue, gl_buffers, N);
clWaitForEvents(N, finish_events);
使用kNumQueues = 1
,该应用程序几乎可以按预期工作:将所有工作收集到一个命令队列中,然后运行,直到GPU一直很忙。通过查看CodeXL分析器的输出,可以看到以下内容:
但是,当我设置时kNumQueues = 2
,我希望发生同样的事情,但是工作会平均分配到两个队列中。如果有的话,我希望每个队列分别具有与一个队列相同的特征:依次开始工作,直到一切完成。但是,当使用两个队列时,我可以看到并非所有工作都分散在两个硬件队列中:
在GPU的工作开始时,队列确实设法异步运行了一些内核,尽管似乎两个队列都没有完全占据硬件队列(除非我误解了)。在GPU工作快要结束时,队列似乎只向一个硬件队列按顺序添加工作,但有时甚至没有内核在运行。是什么赋予了?我对运行时应该如何运行有基本的误解吗?
关于发生这种情况的原因,我有一些理论:
散布的
clCreateBuffer
调用迫使GPU同步地从共享内存池中分配设备资源,这使单个内核的执行停滞了。底层的OpenCL实现不会将逻辑队列映射到物理队列,而仅决定运行时在何处放置对象。
因为我使用的是GL对象,所以GPU需要在写入过程中同步访问对专门分配的内存。
这些假设是否正确?有谁知道在两队列情况下导致GPU等待的原因是什么?任何见解将不胜感激!