在Nvidia硬件上clEnqueueNDRange阻塞? (也是多GPU)

时间:2012-07-19 14:01:34

标签: opencl nvidia

在Nvidia GPU上,当我调用clEnqueueNDRange时,程序会在继续之前等待它完成。更确切地说,我称之为等效的C ++绑定,CommandQueue::enqueueNDRange,但这不应该有所作为。这只发生在Nvidia硬件(3 Tesla M2090s)远程;在我们的办公室工作站上使用AMD GPU,呼叫是无阻塞的,并立即返回。我没有测试的本地Nvidia硬件 - 我们曾经,而且我也记得类似的行为,但它有点模糊。

这使得跨多个GPU的工作更加困难。我已经尝试在新的C ++ 11规范中使用std::async / std::finish为enqueueNDRange的每个调用启动一个新线程,但这似乎也不起作用 - 监视nvidia中的GPU使用情况-smi,我可以看到GPU 0上的内存使用率上升,然后它做了一些工作,然后GPU 0上的内存下降,GPU 1上的内存上升,一个做了一些工作,等等。我的gcc版本是4.7.0。

以下是我启动内核的方法,其中增量是所需的全局工作大小除以设备数量,四舍五入到所需本地工作大小的最接近倍数:

std::vector<cl::CommandQueue> queues;
/* Population of queues happens somewhere
cl::NDrange offset, increment, local;
std::vector<std::future<cl_int>> enqueueReturns;
int numDevices  = queues.size();

/* Calculation of increment (local is gotten from the function parameters)*/

//Distribute the job among each of the devices in the context
for(int i = 0; i < numDevices; i++)
{   
    //Update the offset for the current device
    offset = cl::NDRange(i*increment[0], i*increment[1], i*increment[2]);

    //Start a new thread for each call to enqueueNDRangeKernel
    enqueueReturns.push_back(std::async(
                   std::launch::async,
                   &cl::CommandQueue::enqueueNDRangeKernel,
                   &queues[i],
                   kernels[kernel],
                   offset,
                   increment,
                   local,
                   (const std::vector<cl::Event>*)NULL,
                   (cl::Event*)NULL));
    //Without those last two casts, the program won't even compile
}   
//Wait for all threads to join before returning
for(int i = 0; i < numDevices; i++)
{   
    execError = enqueueReturns[i].get();

    if(execError != CL_SUCCESS)
        std::cerr << "Informative error omitted due to length" << std::endl
}   

内核肯定应该在调用std::async时运行,因为我可以创建一个小的虚函数,在GDB中设置一个断点,并在调用std::async时让它进入它。但是,如果我为enqueueNDRangeKernel创建一个包装器函数,在那里运行它,并在运行后放入一个print语句,我可以看到打印之间需要一些时间。

P.S。由于黑客等原因,Nvidia开发区已经失效,所以我无法在那里发布问题。

编辑:忘了提 - 我作为一个段传递给内核的缓冲区(我上面提到的那个似乎在GPU之间传递的缓冲区)被声明为使用CL_MEM_COPY_HOST_PTR。我一直在使用CL_READ_WRITE_BUFFER,效果相同。

2 个答案:

答案 0 :(得分:2)

我给Nvidia的人发了电子邮件,实际上得到了相当公正的回应。 Nvidia SDK中的示例显示,您需要创建单独的每个设备:

  • 队列 - 您可以代表每个设备并将订单排入其中
  • 缓冲区 - 每个阵列需要一个缓冲区传递给设备,否则设备将传递一个缓冲区,等待它变为可用并有效地序列化所有内容。
  • 内核 - 我认为这是可选的,但它使得指定参数变得更加容易。

此外,您必须为单独线程中的每个队列调用EnqueueNDRangeKernel。这不在SDK样本中,但是Nvidia家伙确认这些电话是封锁的。

完成所有这些后,我在多个GPU上实现了并发性。但是,仍然存在一些问题。 On to the next question...

答案 1 :(得分:0)

是的,你是对的。 AFAIK - nvidia实现具有同步“clEnqueueNDRange”。我在使用我的图书馆(Brahma)时也注意到了这一点。我不知道是否有解决方法或防止这种情况的方法,使用不同的实现(以及设备)保存。