資料層級平行:SIMD、向量與 GPU

CUDA 程式設計模型(CUDA programming model)

/ KOO-dah /

假設你有件工作,是把同一個小任務做一百萬次——替一百萬個像素上色、把一百萬個陣列元素相加。CUDA 是一種告訴 GPU 的方法:「這是一個元素的任務;現在跑它的一百萬份拷貝。」你寫一個簡短的函式描述一個執行緒對一筆資料做什麼,再用單一呼叫啟動那些執行緒的龐大網格。CUDA 是 NVIDIA 寫這類程式的框架(語言擴展加函式庫);OpenCL 是跨廠商的對等物,形狀相同、名稱不同。這一條只做定位導覽——心智模型,不是語法。

這個模型有一個你該描繪出來的乾淨三層階層。你寫的函式是一個核心程式(kernel)——每執行緒的程式。當你啟動它時,你選一個由執行緒區塊(block)組成的網格(grid),每個區塊裝許多執行緒。執行緒便宜又眾多;每個都算出自己的全域索引,用它挑出自己擁有的資料元素。同一區塊裡的執行緒能合作:它們能共用一小塊快速的晶片上暫存記憶體,並在屏障處同步,這就是它們匯集部分結果的方法。在底下,每個區塊的執行緒被切成 warp、以 SIMT 風格執行,所以那個舒適的執行緒畫面,是坐在前幾條提到的 warp 硬體之上。

初學者需要的誠實導覽是這樣。CUDA 讓啟動一百萬個執行緒看起來很容易,但這個模型仍服從 GPU 的本性:執行緒大多要做獨立、規律的工作才會快,warp 內的分支發散照樣讓你付代價,而且有個你不能忽視的記憶體階層——全域裝置記憶體大但相對慢,每區塊的共享暫存記憶體小但快,把它用好是大半的藝術。最重要的是,你的資料起初在 CPU(主機 host)記憶體裡,必須在核心程式執行前複製過去 GPU(裝置 device),執行後再複製回來;在小問題上那次傳輸可能比它換來的運算還貴。CUDA 給你進入吞吐量機器的鑰匙,但它不會改變那台機器擅長與不擅長什麼。

精神上的向量加法:寫一個核心程式,讓全域索引為 i 的執行緒做 c[i] = a[i] + b[i];以譬如 4096 個各 256 執行緒的區塊啟動它,以涵蓋一百萬個元素。每個執行緒恰好處理一個元素;執行期把區塊映射到 GPU 的核心上,並形成 warp 來執行。

寫一個執行緒的程式,啟動一個由執行緒區塊組成的網格;這個模型容易上手,但仍服從 warp、發散與記憶體傳輸的現實。

CUDA 容易的執行緒啟動藏住了硬道理,而非移除它們。發散、全域對共享記憶體的落差,以及主機到裝置的複製都照樣咬人。容易寫不等於快——而且只有在資料值得搬去裝置時,核心程式才有幫助。

又稱
CUDAOpenCLGPU programming modelGPU 程式設計模型