2013-06-12 87 views
4

我想比较单个Intel CPU核心的速度与单个nVidia GPU核心(即:单个CUDA代码,单个线程)的速度。我确实实现了以下naive 2d图像卷积算法:性能问题:单个CPU核心与单个CUDA核心

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阵列传输到共享内存。

我使用的尺寸52x52每2个图像和我得到了以下性能:

  • CPU:10ms的
  • GPU:1338ms
  • GPU(SMEM):1165ms

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

为什么我会得到这样的性能差异?它看起来像一个CUDA核心是这个特定的代码慢100倍!有人可以向我解释为什么?我能想到的原因是

  1. CPU的频率更高
  2. 的CPU确实分支预测。
  3. CPU有更好的缓存机制吗?

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

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

+0

为什么它会是唯一的5-10倍慢?您正在比较两种非常**不同的多线程体系结构。 GPU仅依赖于SIMD(或SIMT)算法。仅使用一个线程对评估GPU的计算能力毫无意义... – BenC

+0

这种“慢5-10倍”是错误的。我将删除它。我不想评估GPU的计算能力。也许我在第一篇文章中不太清楚。我想了解为什么单个CUDA内核和单个CPU内核之间存在如此巨大的性能差异。 – AstrOne

+10

比较CPU上的1个线程与GPU上的1个线程,这意味着只有1个SM的warp调度器。 CPU内核出现故障,具有分支预测,预取,微操作重新排序,L1速度提升10倍,L2速度提升10倍,每周期能够分派6倍指令,核心频率提高4.6倍。费米架构未针对单线程性能进行优化。如果所有内存操作合并,则将线程数增加到32是免费的。由于延迟隐藏,将经纱数量增加至8-12/SM也接近免费。 –

回答

5

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

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中if你运行相同的循环比它不会受益,因为GPU 1线程将再次充当CPU 1线程。而且内存高速缓存,内存复制,数据的开销划分等

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

+0

这是一个很好的解释 – ycomp

-6

为什么会有人试图做这样的事情?对不起,但我不明白....你可以(也绝对应该)运行数千个GPU线程而不是一个! 如果你仍然认为你想创建一个天真的实现,你仍然可以避免两个最外面的for循环。

这是什么意思?如果一个CPU线程不会比一个GPU线程更快,为什么还有人仍然使用它们进行计算?

+1

放松,并再次阅读我的文章。我解释了为什么“地狱”会有人试图做这样的事情。无论如何,我的问题有点被Greg和BenC回答。和平。 – AstrOne

+0

对不起,一定跳过了最后一段:) –