在 Nvidia GPU 上,当我调用 时clEnqueueNDRange
,程序会等待它完成,然后再继续。更准确地说,我将其称为等效的 C++ 绑定,CommandQueue::enqueueNDRange
但这应该没有什么区别。这只发生在远程 Nvidia 硬件(3 Tesla M2090s)上;在我们配备 AMD GPU 的办公室工作站上,呼叫是非阻塞的,并且会立即返回。我没有要测试的本地 Nvidia 硬件——我们曾经做过,我记得当时也有类似的行为,但有点模糊。
这使得将工作分散到多个 GPU 上变得更加困难。std::async
我已经尝试在新的 C++11 规范中使用/为每次调用 enqueueNDRange 启动一个新线程std::finish
,但这似乎也不起作用 - 监控 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 创建一个包装函数,在那里运行它,并在运行后放入一个打印语句,我可以看到打印之间需要一些时间。
PS 由于黑客等原因,Nvidia 开发区已关闭,因此我无法在此处发布问题。
编辑:忘了提 - 我作为参数传递给内核的缓冲区(以及我在上面提到的那个似乎在 GPU 之间传递的缓冲区)被声明为使用 CL_MEM_COPY_HOST_PTR。我一直在使用 CL_READ_WRITE_BUFFER,但效果相同。