我有一个应用程序,我设计在OpenCL上使用AMD GPU运行。终于让应用程序运行并且没有bug(haha)昨天,在一个GPU上进行了目标。现在应用程序正常运行,是时候将它扩展到多个GPU了。
阅读很多关于如何设置它的内容。我们正在使用单一上下文,多队列方法。
我拉出设备列表,然后选择2个GPU并创建一个包含它们的上下文,然后是一个包含这两个设备的BuildProgram。创建两个单独的队列。
原始工作应用程序的伪代码,现在转换为处理2 gpus:
context = clCreateContext(0, 2, device_list, NULL, NULL, &ret);
for(x = 0; x < 2; x++)
queue[x] = clCreateCommandQueue(context, device_list[x], ENABLE_PROFILING, &ret);
clBuildProgram(program, 2, device_list, options, NULL, NULL);
create kernels..
run...
for(outer_loop = 0; outer_loop < 10; outer_loop++) {
clEnqueueNDRangeKernel(queue[0], kernel_init, offset, &event[0]);
clEnqueueNDRangeKernel(queue[1], kernel_init, different_offset, &event[1]);
clFinish(queue[0]);
clFinish(queue[1]);
get profiling data and printf results
}
这基本上就是代码的外观。设置参数并在循环之前完成写入 - init内核不依赖输入来开始工作。它运行后,它会将生成的数据的async_work_group_copy执行到全局缓冲区。
现在,在我修改2个GPU的代码之前,内核在27ms内运行(对于每个循环)
在我修改代码后,如果我注释掉2个内核运行中的一个或另外一个(EnqueueNDRangeKernel和相关的clFinish),它们将在27ms内运行。
如果我运行代码并行运行在两个GPU上,我会得到非常奇怪的行为。
循环中的第一次运行,它们都分别在大约37-42ms内执行。因为我完成了两倍的工作,所以我可以稍稍放慢速度。但是在第一次运行之后,一个或另一个内核将在排队和启动之间随机延迟4-5秒。
这是我的分析/时间输出。所有数字均以毫秒为单位。
Q0: til sub: 8.8542 til start: 9.8594 til fin: 47.3749
Q1: til sub: 0.0132 til start: 13.4089 til fin: 39.2364
Q0: til sub: 0.0072 til start: 0.2310 til fin: 37.1187
Q1: til sub: 0.0122 til start: 4152.4638 til fin: 4727.1146
Q0: til sub: 0.0302 til start: 488.6218 til fin: 5049.7233
Q1: til sub: 0.0179 til start: 5023.9310 til fin: 5049.7762
Q0: til sub: 0.0190 til start: 2.0987 til fin: 39.4356
Q1: til sub: 0.0164 til start: 3996.2654 til fin: 4571.5866
Q0: til sub: 0.0284 til start: 488.5751 til fin: 5046.3555
Q1: til sub: 0.0176 til start: 5020.5919 til fin: 5046.4382
我正在运行的机器上有5个GPU。无论我使用哪两个,两个GPU中的一个(它不总是相同的)在启动时会有4-5秒的延迟。使用单个GPU - 没有延迟。
可能导致这种情况的原因是什么?任何的想法?我没有阻止 - clFinish只是为了获取分析信息。即使它被阻止也不会是5秒的延迟。
另外 - 我想也许内核正在进行的全局写操作可能是延迟的一部分。我评论了这些写作。不。没有变化。
事实上,我加了回报;作为内核的第一行 - 所以它绝对没有。 40毫秒下降到.25,但5秒延迟仍然存在。
答案 0 :(得分:2)
OpenCL驱动程序并不关心内核中发生的事情。如果内核写/读或是空内核,或者它只写入缓冲区的一个部分。它关心缓冲区参数标志,并确保数据在GPU之间保持一致,如果内核在其他内核中具有任何依赖性,则会阻止内核。 GPU到GPU的传输是透明的,而且成本非常高。
使用多个GPU时,必须认真对待隐藏的数据复制和同步,因为这通常是主要的瓶颈。
如果您的内核可以并行运行(因为GPU1处理的是与GPU2上的数据不同的数据,等等......),那么您应该为每个GPU创建不同的缓冲区。或者,如果数据相同,请正确设置类型CL_READ_ONLY
/ CL_WRITE_ONLY
,以确保正确的OpenCL行为。最小的复制/一致性操作。
例如对于这些内核:
kernel Sum(read_only A, read_only B, write_only C);
kernel Sum_bad(read_write A, read_write B, write_only C);
如果您使用单个GPU,两者的行为将完全相同,因为所有内存都驻留在同一GPU中。 但是使用多个GPU可能会导致严重的问题,例如:
Queue 1/GPU 1: Sum_Bad(A,B,C);
Queue 2/GPU 2: Sum_Bad(A,D,E);
事件将发生如下:
如您所见,GPU2必须等待第一个完成,并等待所有参数复制回来。 (可能是5s?也许,取决于尺寸)
然而,使用正确的方法:
Queue 1/GPU 1: Sum(A,B,C);
Queue 2/GPU 2: Sum(A,D,E);
事件将发生如下:
并行(因为没有依赖性)