2013-06-12 123 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

對不起,一定跳過了最後一段:) –