【问题标题】:Performance issues: Single CPU core vs Single CUDA core性能问题:单 CPU 内核与单 CUDA 内核
【发布时间】:2013-06-08 02:37:51
【问题描述】:

我想比较单个 Intel CPU 内核的速度与单个 nVidia GPU 内核的速度(即:单个 CUDA 代码、单个线程)。我确实实现了以下简单的 2d 图像卷积算法:

void convolution_cpu(uint8_t* res, uint8_t* img, uint32_t img_width, uint32_t img_height, uint8_t* krl, uint32_t krl_width, uint32_t krl_height)
{
    int32_t center_x = krl_width  / 2;
    int32_t center_y = krl_height / 2;
    int32_t sum;
    int32_t fkx,fky;
    int32_t xx,yy;

    float krl_sum = 0;
    for(uint32_t i = 0; i < krl_width*krl_height; ++i)
        krl_sum += krl[i];
    float nc = 1.0f/krl_sum;

    for(int32_t y = 0; y < (int32_t)img_height; ++y)
    {
        for(int32_t x = 0; x < (int32_t)img_width; ++x)
        {
            sum = 0;

            for(int32_t ky = 0; ky < (int32_t)krl_height; ++ky)
            {
                fky = krl_height - 1 - ky;

                for(int32_t kx = 0; kx < (int32_t)krl_width; ++kx)
                {
                    fkx = krl_width - 1 - kx;

                    yy = y + (ky - center_y);
                    xx = x + (kx - center_x);

                    if( yy >= 0 && yy < (int32_t)img_height && xx >= 0 && xx < (int32_t)img_width )
                    {
                        sum += img[yy*img_width+xx]*krl[fky*krl_width+fkx];
                    }
                }
            }
            res[y*img_width+x] = sum * nc;
        }
    }
}

CPU 和 GPU 的算法相同。我还制作了另一个与上面几乎相同的 GPU 版本。唯一的区别是我在使用 imgkrl 数组之前将它们转移到共享内存中。

我使用了 2 张尺寸为 52x52 的图像,我得到了以下性能:

  • CPU:10 毫秒
  • GPU:1338 毫秒
  • GPU(内存):1165 毫秒

CPU 是 Intel Xeon X5650 2.67GHz,GPU 是 nVidia Tesla C2070。

为什么我会得到这样的性能差异?对于这个特定的代码,单个 CUDA 内核看起来要慢 100 倍!有人可以向我解释为什么吗?我能想到的原因是

  1. CPU 的更高频率
  2. CPU 进行分支预测。
  3. CPU 可能有更好的缓存机制?

您认为造成这种巨大性能差异的主要问题是什么?

请记住,我想比较单个 CPU 线程和单个 GPU 线程之间的速度。我并不是要评估 GPU 的计算性能。我知道这不是在 GPU 上进行卷积的正确方法。

【问题讨论】:

  • 为什么它只会慢 5-10 倍?您正在比较两个 非常 不同的多线程架构。 GPU 仅依赖于 SIMD(或 SIMT)算法。仅使用一个线程来评估 GPU 的计算能力绝对没有意义......
  • 这个“慢 5-10 倍”是错误的。我会删除它。我并不是要评估 GPU 的计算能力。也许我在第一篇文章中不是很清楚。我试图理解为什么单个 CUDA 内核和单个 CPU 内核之间存在如此巨大的性能差异。
  • 将 CPU 上的 1 个线程与 GPU 上的 1 个线程进行比较,这意味着只有 1 个 SM 的 warp 调度程序。 CPU 内核出现故障,具有分支预测、预取、微操作重排序,L1 快 10 倍,L2 快 10 倍,每个周期调度指令多 6 倍,内核频率快 4.6 倍。 Fermi 架构并未针对单线程性能进行优化。如果合并所有内存操作,则将线程数增加到 32 是免费的。由于延迟隐藏,将 warp 计数增加到 8-12/SM 也接近免费。
  • 感谢 BenC 和 Greg 的回复。所以,如果我们假设我的 GPU 只有一个 CUDA 核心,我的代码并没有真正的错误,对吧?这只是 CPU 更复杂的事实。
  • 他们都有自己的特点。 CPU 线程非常适合任务并行性,而 GPU 线程将在数据并行性方面脱颖而出。必须了解 CPU 和 GPU 的底层架构才能使任何比较有价值。 SM 无法以比 32(warp)更细的粒度执行指令,因此即使您认为只使用一个线程,实际上也有 31 个线程在等待,什么也不做。你不会用电锯修剪盆景树。好吧,你也不会在 GPU 上只使用一个线程。

标签: performance cuda gpgpu convolution


【解决方案1】:

我想解释一下,可能对你有用。

CPU 充当主机,GPU 充当设备。

为了在 GPU 上运行线程,CPU 将所有数据(计算 + 将在其上执行计算的数据)复制到 GPU。这个复制时间总是大于计算时间。因为计算是在 ALU-算术和逻辑单元中执行的。这只是一些说明。但是复制需要更多时间。

因此,当您在 CPU 中仅运行一个线程时,CPU 会将所有数据存储在自己的内存中,拥有自己的缓存以及分支预测、预取、微操作重新排序、L1 快 10 倍、L2 快 10 倍、调度能力每个周期多 6 倍的指令,快 4.6 倍的核心频率。

但是当你想在 GPU 上运行线程时,它首先将数据复制到 GPU 内存上。这一次需要更多的时间。其次,GPU 内核在一个时钟周期内运行线程网格。但为此我们需要对数据进行分区,以便每个线程都可以访问一个数组项。在您的示例中,它是 img 和 krl 数组。

还有一个可用于 nvidia GPU 的分析器。删除代码中的打印输出或打印等代码(如果存在)并尝试分析您的 exe。它会以毫秒为单位显示复制时间和计算时间。

循环并行化:当您使用 image_width 和 image_height 运行两个循环来计算图像时,需要更多的时钟周期来执行,因为在指令级它会通过计数器运行。但是当您将它们移植到 GPU 上时,您使用 threadid.x 和 threadid.y 以及 16 或 32 个线程的网格,这些线程仅在 GPU 的一个核心中运行一个时钟周期。这意味着它在一个时钟周期内计算 16 或 32 个数组项,因为它有更多的 ALU。(如果不存在依赖关系并且数据分区良好)

在您的卷积算法中,您在 CPU 中维护了循环,但在 GPU 中,如果您运行相同的循环,它将不会受益,因为 GPU 1 线程将再次充当 CPU 1 线程。还有内存缓存、内存复制、数据分区等开销。

我希望这会让你明白......

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-06-22
    • 2013-02-01
    • 1970-01-01
    • 1970-01-01
    • 2011-08-24
    • 2018-03-06
    • 2019-04-10
    • 2019-06-20
    相关资源
    最近更新 更多