nVidia CUDA 簡介


先碎碎念一下…以目前來講,CUDA 還算是一個很新的技術,在網路上可以找到的教學文件,相對也就很少。目前 nVidia 官方的文件,也只有一份 Programming Guide 和 SDK 裡附的 Sample,連份 tutorial 都沒有,對於初學者來說,要上手其實還滿麻煩的。在 nVidia 的論壇中,其實有比較多的資料,但是畢竟是以論壇的形式存在的,沒有整理過,要找資料可以,要拿來學習並不是很容易。

Heresy 對於 CUDA 也是剛開始玩,也只能參考 Programming Guide 和 sample code 來摸索;希望以後,能找到更多更好的教學文件可以做參考。


GPGPU

在近年來,顯示卡的運算單元(GPU)的速度越來越快,在某些方面的應用上,甚至已經大幅的超越 CPU 了!右圖就大致說明了目前 GPU 和 CPU 的速度演進~很明顯的可以看出來,單就浮點數的計算來說,GPU 的成長速度已經算是 CPU 的數倍了!(電晶體數目也大增、耗電量也大增… @@)

再加上 shader 程式的出現、演進,GPU 的可控制性也大幅增加!因此,將 GPU 用在非傳統的 3D 圖形顯示方面的應用,也越來越多了…一般,會把這樣的應用叫做 GPGPU(General-purpose computing on graphics processing units);而相關的應用,不管是數量和方向,都相當多了!有興趣的人可以參考維基百科

原則上,適用於 GPGPU 的問題,大多是用在可以把一個問題大量的拆解成多個相同、且彼此不相關的小問題的情況;在這種情況下,用 GPGPU 的方法,就可以把這些一樣的小問題,丟給顯示卡的 GPU 來大量平行化的處理,藉此達到加速的效果。

更明確的說,由於 GPU 的架構設計,GPU 最適合拿來做的計算是「資料平行化的計算(data-parallel computations)」;也就是不同的資料,要用同樣的程式來做計算。而在這種情況下,就可以透過把每一組資料的計算,都當成一個 thread 來計算,以此平行化來加速計算。其中,個別的計算量也要較高,如此才能避免平行話的負擔大於平行化的加速。

傳統的 GPGPU 的開發方法,都是透過 OpenGL 或 Direct3D 這一類現有的圖形函式庫,以編寫 shading language 的方法,控制 shader 來想辦法做到自己想要的計算;不過這樣的缺點,就是必須要遵循著 Redner 的固定流程來進行。

CUDA

而 nVidia 所提出的 CUDA(Compute Unified Device Architecture) 也是一種 GPGPU 的技術;不過,透過 CUDA 來進行 GPGPU 的程式開發,是透過他的 C 語言的函式庫和一些 CUDA 的延伸來編寫,因此不用用到 OpenGL 或 Direct3D。以 nVidia 的官方說法來說,是說入門的門檻會相對的降低;此外也因為不用使用圖形函式庫,而不會被傳統的 render pipeline 綁住使得在程式設計上更方便。

CUDA 的架構圖大概如下:

其中,CUDA 大概分為 Library、runtime、Driver 三個部分;而我們在開發程式的時候,可以透過這三個部分,來使用 GPU 的計算能力。

而由於 CUDA 是個新的技術,目前只能用在 nVidia G80 核心的顯示卡上,也就是 GeForce 8 系列,以及最新的 Quadro FX 了~不過,nVidia 也承諾現在用 CUDA 寫的程式,在將來新的顯示卡上也會可以正常運作。

Programming Model

在 CUDA 的程式架構裡,程式執行的區域會分成兩部分:

  • Host
  • Device

其中,「host」指的就是 CPU,而「device」就是 GPU 了~

在 CUDA 的程式架構中,主程式還是由 CPU 來執行;而當遇到了資料平行化處理的部分,就會將要在 GPU 跑的程式編譯成 device 能執行的程式,再丟給 device 執行了。而這個程式在 CUDA 裡把他叫做「kernel」。

而實際在運作時,CUDA 會產生許多在 device 上執行的 thread,每一個 thread 都會去執行 kernel 這個程式;雖然程式都是同一份,但是會因為其 index 的不同,而取得不同的資料來計算。

在 device 中要執行的 thread 中,要根據「最有效率的資料共用」來建置 thread 的 block;其中,thread block 的型式可以是一維、二維或三維的。而在同一個 block 裡的 thread,有部分的記憶體(shared memory)是共用的;所以在校能的調整上,可能須要考慮到這一點。

而由於 thread block 的最大大小是有限制的,所以不能把所有的 thread 都塞到同一個 block 裡(一般的 GPGPU 程式 thread 數目都會很多);這時候,可以用同樣維度和大小的 thread block(當然,也要是同一個 kernel),來組成一個 grid 做批次處理。這個 grid 可以是一維或二維陣列的形式。

所以,實際上 CUDA 在執行一段 kernel 程式的時候,必須要指定他的 grid 和 block 的相關資訊(維度和大小);而會產生的 thread 數目,就會由這兩者的資訊來決定。

Memory Model

在 CUDA 中,要讓 thread 可以使用的變數,都必須要先把資料放置到 device 的記憶體裡;而 device 的記憶體,又分為 DRAM 和 chip 上的記憶體兩種。在實際使用上,分成下面幾種記憶體的類型:

  • registers
    Read-write per-thread
  • local memory
    Read-write per-thread
  • shared memory
    Read-write per-block
  • global memory
    Read-write per-grid
  • constant memory
    Read-only per-grid
  • texture memory
    Read-only per-grid

可以發現,registerslocal memory 是以 thread 為單位來使用的;而 shared memory 則是存在於 block,讓每一個 thread 共用。上面這三種,都是在 chip 上的記憶體,相較之下,速度會比屬於 DRAM 的記憶體來的快。(注意!本文當時的參考資料可能有誤。local memory 實際上並不是 on-chip 的,而是在 DRAM 上,讀取速度也不快。細節請參考本文的回應)

Grid 中的 global, constant, texture 這三種 memory 可以讓不同的 block 中的 thread 一起使用。此外,這三種記憶體則是屬於 DRAM 上的記憶體,可以在同一個程式的不同 kernel 中,持續的存在、使用;而他們之間的差異,在於最佳化的方式不同。像 constant 和 texture 因為對於 thread 是唯獨的,所以實際上會有快取的機制,可以用來加速讀取。


上面的文章大致對 CUDA 的架構和程式的基本概念做了一些說明,內容大概對映到 nVidia 的《CUDA Programming Guide 1.0》(PDF 文件)的第一章和第二章,不過省略了不少東西;而本文的圖片也都取自於該文件。第三張硬體實作的部分應該也會跳過,接下來就開始寫 CUDA 程式了!

參考文件:


對「nVidia CUDA 簡介」的想法

  1. 感謝您的回應,另外我還有一個問題。

    我嘗試使用不同的memory種類 來做image transpose的工作

    一開始我是依照 global memory, shared memory, texture memory的順序來執行

    (在同一個程式內 做相同的工作 但用3種memory)

    速度快慢為 texture memory, share memory, global memory (其中texture速度約是global 的兩倍快)

    在第二次我改變其順序>>texture memory, global memory, shared memory,

    速度快慢變為 global memory, shared memory, texture memory

    其中 shared memory的時間跟一開始的差不多,但global 卻比 texture快約兩倍

    想請問Heresy,global 跟 texture 之間的cache是否有共用??且kernel做完後,cache的資料會繼續

    留著,所以後者的memory速度會比較快。還是有其他原因???

    謝謝您

    • 不太確認你的測試方法?不過個人會建議,如果真的要準確的話,測完之後重開機再測試下一項,應該會比較準確。
      而理論上 Global 和 texture 的 cache 應該是不同的。

  2. Heresy 你好,我最近嘗試在做影像處理的cuda實現。由於我需要使用updated的資料來做濾波(比如說 我的輸入影像A,在A(x,y)處理完的值必須填回A(x,y),則下一個點A(x,y+1)會考慮A(x,y)的值去做濾波處理,同理下一個點A(x,y+2)也會考慮A(x,y+1)的點),由於這項處理是sequential的作法,我嘗試以1個block內只有1個thread的做法丟入kernel,但好像因為thread是會random的丟入SM做處理,並不會依序處理(???不確定),所以結果是失敗的。

    想請問大大對於這類的工作,是否有其他方式去實現。
    謝謝你~

    • CUDA 基本上是為了大量平行化而設計的。
      如果你的計算是需要依序進行、無法平行化的話,基本上是不適用 CUDA 來做加速的。

  3. […] 說實話,個人感觸滿多的…雖然該書的其他作者大多不認識,但是其中,開勇也算是 Heresy 認識的人了~同樣都是滿早期就開始玩 nVidia CUDA、也都有在部落格上進行一連串發表,也曾經有些交流。而它在這方面的經歷、投注,真的比 Heresy 多很多啊… […]

發表留言

這個網站採用 Akismet 服務減少垃圾留言。進一步了解 Akismet 如何處理網站訪客的留言資料