【问题标题】:clEnqueueNDRange blocking on Nvidia hardware? (Also Multi-GPU)clEnqueueNDRange 在 Nvidia 硬件上阻塞? (也是多 GPU)
【发布时间】:2012-07-18 17:42:57
【问题描述】:

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

这使得将工作分散到多个 GPU 上变得更加困难。我已经尝试在新的 C++11 规范中使用 std::async/std::finish 为每次调用 enqueueNDRange 启动一个新线程,但这似乎也不起作用 - 监控 nvidia-smi 中的 GPU 使用情况,我可以看到 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 创建一个包装函数,在那里运行它,并在运行后放入一个打印语句,我可以看到打印之间需要一些时间。

附:由于黑客等原因,Nvidia 开发区已关闭,因此我无法在此处发布问题。

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

【问题讨论】:

    标签: opencl nvidia


    【解决方案1】:

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

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

    此外,您必须在单独的线程中为每个队列调用 EnqueueNDRangeKernel。这不在 SDK 示例中,但 Nvidia 人员确认调用被阻塞。

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

    【讨论】:

      【解决方案2】:

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

      【讨论】:

      • 那么,这是否意味着 clEnqueueNDRange 在任何给定的上下文中一次只能被调用一个?正如我对std::async 的实验所暗示的那样,情况似乎就是这样。编辑:或者,更确切地说,不是一次调用一个,一次执行一个。
      • 根据我的实验,是的。每当我想做更复杂的事情时,我都会使用 AMD(或 Intel)实现。但是,您可以创建两个上下文,每个上下文都有一个设备,对吗?
      • 好吧 dangit。我的设置的问题是我需要此代码在多 GPU 设置上运行,并且我可以访问的两个 GPU 集群都在其中安装了 Nvidia 卡。如果没有解决方法,那我就死定了。在他们的论坛关闭时通过电子邮件发送 Nvidia 的支持是否值得? arghedit:是的,我想我可以尝试不同的上下文。
      • 我会尝试的——但我最后一次尝试(可能是 8 个月前),他们回来了——嘿,试试 CUDA,效果很好。 pfft
      • 哈,好吧。在我给你打勾之前,我想我会把这个问题留得更久,看看是否有其他人有解决方法。
      猜你喜欢
      • 2014-05-30
      • 2011-12-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-07-03
      • 2012-06-13
      相关资源
      最近更新 更多