使用多个GPU时OpenCL内核START会延迟吗?

时间:2015-07-20 14:44:43

标签: opencl gpu multiple-gpu

我有一个应用程序,我设计在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秒延迟仍然存在。

1 个答案:

答案 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);

事件将发生如下:

  1. 内存A,B将被复制到GPU1内存(如果它已经存在)。 GPU1中分配的C内存。
  2. GPU 1将运行内核。
  3. 内存A将从GPU1复制到GPU2。内存D将被复制到GPU2。内存E已分配。
  4. GPU2将运行内核。
  5. 如您所见,GPU2必须等待第一个完成,并等待所有参数复制回来。 (可能是5s?也许,取决于尺寸)

    然而,使用正确的方法:

    Queue 1/GPU 1: Sum(A,B,C);
    Queue 2/GPU 2: Sum(A,D,E);
    

    事件将发生如下:

    1. 内存A,B将被复制到GPU1内存(如果它已经存在)。 GPU1中分配的C内存。
    2. GPU 1将运行内核。
    3. 并行(因为没有依赖性)

      1. 内存A,D将被复制到GPU2(如果它已经存在)。内存E已分配。
      2. GPU2将运行内核。