我正在编写一个openCL应用程序,其中我有N个工作项,我想分发给D设备,其中N> D并且反过来每个设备可以并行处理其自己的工作项的元素,从而实现一种“双重”并行性。
这是我已经编写的代码,试图实现这一目标。
首先,我为每个设备创建一个事件,并将它们全部设置为完成:
cl_int err;
cl_event *events = new cl_event[deviceCount];
for(int i = 0; i < deviceCount; i++)
{
events[i] = clCreateUserEvent(context, &err);
events[i] = clSetUserEventStatus(events[i], CL_COMPLETE);
}
每个设备也有自己的命令队列和自己的内核“实例”。
然后我进入我的“主循环”来分发工作项。代码查找第一个可用设备并将其与工作项一起排队。
/*---Loop over all available jobs---*/
for(int i = 0; i < numWorkItems; i++)
{
WorkItem item = workItems[i];
bool found = false; //Check for device availability
int index = -1; //Index of found device
while(!found) //Continuously loop until free device is found.
{
for(int j = 0; j < deviceCount; j++) //Total number of CPUs + GPUs
{
cl_int status;
err = clGetEventInfo(events[j], CL_EVENT_COMMAND_EXECUTION_STATUS, sizeof(cl_int), &status, NULL);
if(status == CL_COMPLETE) /*Current device has completed all of its tasks*/
{
found = true; //Exit infinite loop
index = j; //Choose current device
break; //Break out of inner loop
}
}
}
//Enqueue my kernel
clSetKernelArg(kernels[index], 0, sizeof(cl_mem), &item);
clEnqueueNDRangeKernel(queues[index], kernels[index], 1, NULL, &glob, &loc, 0, NULL, &events[index]);
clFlush(commandQueues[index]);
}
然后我最后通过在我的所有设备上调用clFinish来结束:
/*---Wait For Completion---*/
for(int i = 0; i < deviceCount; i++)
{
clFinish(queues[i]);
}
这种方法存在一些问题:
1)它不会将作品分发给我的所有设备。在我目前的电脑上,我有3台设备。我上面的算法只将工作分配给设备1和2.设备3总是被遗漏,因为设备1和2完成得如此之快,以至于他们可以在3次获取机会之前抢夺更多工作项。
2)即使设备1和2一起运行,我也只看到非常非常温和的速度提升。例如,如果我要将所有工作项分配给设备1,则可能需要10秒才能完成,如果我将所有工作项分配给设备2,则可能需要11秒才能完成,但如果我尝试在它们之间拆分工作,结合它可能需要8-9秒,我希望可能在4-5秒之间。我觉得他们可能并不像我想要的那样彼此并行运行。
如何解决这些问题?
答案 0 :(得分:3)
您必须小心尺寸和内存位置。通常,在处理GPU设备时不考虑这些因素。我会问你:
他们完成的速度有多快?
内核是否独立?他们使用不同的缓冲区吗?
主机是否是瓶颈?
永远不要让设备清空
我愿意:
将此行更改为已提交的作业:if(status >= CL_SUBMITTED)
确保设备是有序的GPU - &gt;中央处理器。因此,GPU是设备0,1,CPU是设备2。