先碎碎念一下…以目前來講,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可以發現,registers 和 local 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 Homepage
- CUDA Programming Guide 1.0
- CUDA @ Wikipedia
- GPGPU Site
- Introduction to NVIDIA CUDA @Siggraph 2007
- NVIDIA CUDA Performance @Siggraph 2007
- Supercomputing 2007 CUDA Tutorial
- ARCS 2008 GPGPU Tutorial
[…] Heresy 個人算是滿早期開始接觸 NVIDIA CUDA 的,但是其實已經沒有在碰 NVIDIA CUDA […]
讚讚
[…] global memory 來直接進行存取。不過實際上,有的時候還有別的選擇的~在《nVidia CUDA 簡介》中一文就有提到,除了 global memory 外,還可以透過 constant memory 或 texture […]
讚讚
[…] NVIDIA 除了針對一般使用者的 GeForce 系列外,實際上還有專為大量平行計算設計的 Tesla(官網)、以及專業繪圖的 Quadro 系列(官網)。當然目前還有 […]
讚讚
感謝您的回應,另外我還有一個問題。
我嘗試使用不同的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 應該是不同的。
讚讚
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 來做加速的。
讚讚
[…] nVIDIA CUDA 和 OpenCL 外,不管是像 Thrust 或 Bolt 這種以函式庫形式出發的,還是 C++AMP […]
讚讚
[…] OpenACC 一樣簡單,但是相較於 nVIDIA CUDA 或 […]
讚讚
[…] 說實話,個人感觸滿多的…雖然該書的其他作者大多不認識,但是其中,開勇也算是 Heresy 認識的人了~同樣都是滿早期就開始玩 nVidia CUDA、也都有在部落格上進行一連串發表,也曾經有些交流。而它在這方面的經歷、投注,真的比 Heresy 多很多啊… […]
讚讚
[…] Group 在新版的 PGI 編譯器(Fortran 和 C 語言用)裡,將支援 nVidia CUDA 的 GPU 加速!而目前 x64 Linux 版應該已經可以先透過「PGI Accelerator Compiler […]
讚讚
[…] 是 nVidia Performance Primitives 的縮寫,是一個 nVidia 官方推出用來加速 CUDA […]
讚讚