【问题标题】:OpenCL MultiGPU slower than single GPUOpenCL MultiGPU 比单 GPU 慢
【发布时间】:2014-05-26 17:02:32
【问题描述】:

我正在开发一个对视频帧数据执行一些处理的应用程序。为了加速它,我使用了 2 个图形卡并使用 OpenCL 处理数据。我的想法是将一帧发送到第一张卡,另一帧发送到第二张卡。这些设备使用相同的上下文,但命令队列、内核和内存对象不同。

但是,在我看来,计算不是并行执行的,因为 2 张卡所需的时间几乎与仅一张显卡所需的时间相同。

有没有人有一个很好的例子,可以同时在独立的数据片段上使用多个设备?

提前致谢。

编辑:

这是切换到 2 个独立上下文后的结果代码。但是,使用 2 个显卡的执行时间仍然与使用 1 个显卡的相同。

    cl::NDRange globalws(imageSize);
    cl::NDRange localws;

    for (int i = 0; i < numDevices; i++){
            // Copy the input data to the device
            commandQueues[i].enqueueWriteBuffer(inputDataBuffer[i], CL_TRUE, 0, imageSize*sizeof(float), wt[i].data);

            // Set kernel arguments
            kernel[i].setArg(0, inputDataBuffer[i]);

            kernel[i].setArg(1, modulusBuffer[i]);
            kernel[i].setArg(2, imagewidth);
        }

        for (int i = 0; i < numDevices; i++){
            // Run kernel
            commandQueues[i].enqueueNDRangeKernel(kernel[i], cl::NullRange, globalws, localws);
        }

        for (int i = 0; i < numDevices; i++){
            // Read the modulus back to the host
            float* modulus = new float[imageSize/4];
            commandQueues[i].enqueueReadBuffer(modulusBuffer[i], CL_TRUE, 0, imageSize/4*sizeof(float), modulus);

            // Do something with the modulus;
        }

【问题讨论】:

  • 抱歉,只有指向 developer.nvidia.com/opencl#oclSimpleMultiGPU 的指针,以及有一段时间(在 CUDA 3.0 左右,很久以前)NVIDIA 卡无法在 Win Vista/7 上同时运行的提示,. ..但我希望他们能解决这个问题。
  • @Marco13 当前的 Nvidia 和 AMD 图形驱动程序允许在 Windows 7 和 8 上的同一应用程序中同时使用多个 GPU(可能也是 Vista,但我尚未对此进行测试)。
  • @krisg 在没有看到代码的情况下,我只能提出一般性建议:不要使用任何阻塞 API 调用,仅在将工作分派到所有设备后使用 clWaitForEvents/clFinish。为两个设备使用单个上下文的一个副作用是,您分配的缓冲区和图像将在两个设备上,即两者都将具有第 1 帧和第 2 帧的内存对象,即使每个设备只需要一帧。就个人而言,我只是为每个设备使用单独的上下文。
  • 感谢两位的回答。切换到不同的上下文确实很有意义。我会尝试一下并发布一些代码并稍后报告结果。

标签: opencl multi-gpu


【解决方案1】:

您的主要问题是您正在使用阻塞呼叫。如果您以这种方式操作它们,那么您拥有多少设备并不重要。由于您正在执行操作并等待它完成,因此根本没有并行化(或很少)。你现在正在这样做:

Wr:-Copy1--Copy2--------------------
G1:---------------RUN1--------------
G2:---------------RUN2--------------
Re:-------------------Read1--Read2--

你应该至少改变你的代码来做到这一点:

Wr:-Copy1-Copy2-----------
G1:------RUN1-------------
G2:------------RUN2-------
Re:----------Read1-Read2--

使用此代码:

cl::NDRange globalws(imageSize);
cl::NDRange localws;

for (int i = 0; i < numDevices; i++){
        // Set kernel arguments //YOU SHOULD DO THIS AT INIT STAGE, IT IS SLOW TO DO IT IN A LOOP
        kernel[i].setArg(0, inputDataBuffer[i]);

        kernel[i].setArg(1, modulusBuffer[i]);
        kernel[i].setArg(2, imagewidth);

        // Copy the input data to the device
        commandQueues[i].enqueueWriteBuffer(inputDataBuffer[i], CL_FALSE, 0, imageSize*sizeof(float), wt[i].data);
    }

    for (int i = 0; i < numDevices; i++){
        // Run kernel
        commandQueues[i].enqueueNDRangeKernel(kernel[i], cl::NullRange, globalws, localws);
    }

    float* modulus[numDevices];
    for (int i = 0; i < numDevices; i++){
        // Read the modulus back to the host
        modulus[i] = new float[imageSize/4];
        commandQueues[i].enqueueReadBuffer(modulusBuffer[i], CL_FALSE, 0, imageSize/4*sizeof(float), modulus[i]);
    }

    clFinish();

        // Do something with the modulus;

关于 cmets 是否具有多个上下文,取决于您是否打算同时与两个 GPU 通信。只要 GPU 只使用它们的内存,就不会有复制开销。但是,如果您不断设置/取消设置内核参数,则会触发复制到其他 GPU。所以,要小心。

GPU 之间不通信的更安全方法是不同的上下文。


我怀疑您的主要问题是内存副本而不是内核执行,如果您隐藏内存延迟,很可能 1 个 GPU 将满足您的需求:

Wr:-Copy1-Copy2-Copy3----------
G1:------RUN1--RUN2--RUN3------
Re:----------Read1-Read2-Read3-

【讨论】:

  • 感谢您的回答。在使用 1 个 GPU 的情况下,使用您的代码和提示也会产生更好的结果。每个图像的内核执行大约需要 4 毫秒,而数据传输大约需要 2 毫秒。一个 GPU 就足够了。我只是想评估性能并比较 1 个和 2 个 GPU 的结果。不幸的是,即使我应用了您提出的更改,1 和 2 个 GPU 之间的执行时间仍然没有显着差异。
  • 2ms 传输和 4ms 执行是相当大的 IO 开销。也许您应该尝试使用固定内存。尝试首先在 GPU 操作(而不是 IO)中找到瓶颈,然后才能检查多 GPU 方法。检查您的方法的另一个选项是删除 IO 副本(不要读取或写入数据)。通过这种方式,您将看到多 GPU 是否比单 GPU 更快。 (应该)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-03-05
  • 1970-01-01
  • 2013-05-02
  • 2014-01-17
相关资源
最近更新 更多