CUDA 入門練習題 (Practice - Introduction to CUDA)
Related Concepts
- 01-Introduction-to-CUDA/01-GPU-Computing-Foundations
- 01-Introduction-to-CUDA/02-Execution-Model-and-SIMT
- 01-Introduction-to-CUDA/03-Tile-Programming
- 01-Introduction-to-CUDA/04-GPU-Memory-Hierarchy
- 01-Introduction-to-CUDA/05-CUDA-Platform
| 關鍵字 / 情境 | 重點 |
|---|---|
| CUDA 推出年份 / 全名 | 2006;Compute Unified Device Architecture(2003 = 部分 pipeline 可程式化) |
| GPU vs CPU | GPU 高 throughput + memory bandwidth、數千 threads;CPU 低 latency、序列、少量 threads |
| 上手途徑 | libraries(cuBLAS/cuFFT/cuDNN/CUTLASS)、AI frameworks、DSLs(Warp/Triton) |
| host / device | host = CPU + host memory;device = GPU + device memory;程式永遠從 CPU 開始 |
| kernel / launch | 在 GPU 執行的函式;launch = 平行啟動大量 threads 跑 kernel code |
| thread 階層 | thread → warp(32) → thread block(單一 SM)→ grid;cluster 為 CC 9.0+ 選用 |
| SIMT / divergence | Single-Instruction Multiple-Threads;同 warp 走不同分支 → lane 被 mask,利用率下降 |
| block 排程 | 每 block 在單一 SM 跑到完成;block 間無順序保證、無資料相依,須可任意順序執行 |
| Tile 模型 | per-block 寫程式描述對 tiles 的運算;compiler 決定 threads/block;無 warp divergence |
| Array vs Tile | array = device memory、mutable;tile = block 區域、immutable、維度為 2 的次方且編譯期已知、不可當 kernel 參數 |
| load / store 越界 | load 可補零;store 越界被丟棄(不對稱) |
| global memory | device code 視角下 GPU 的 DRAM;GPU 內所有 SM 可存取 |
| register / shared | register per-thread、compiler 配置;shared memory per-block、供 block 內交換資料 |
| cache | L1 per-SM;L2 全 GPU 共享;constant cache per-SM(可放 kernel 參數) |
| unified vs mapped | unified memory 可 CPU/GPU 存取、runtime 搬移;mapped memory 走 PCIe/NVLINK,延遲高、非高效替代 |
| CC / SM / PTX / cubin | CC X.Y 直接對應 SM 版本(CC 12.0 → sm_120);compute_XY = PTX,sm_XY = cubin |
| 相容性 | binary:同 major 內 minor >= 可載入、跨 major 不相容;PTX:JIT 到相同或更高 CC(forward compat) |
Question 1 - GPU 演進與 CUDA 起源 [recall]
請說明 GPU 繪圖 pipeline「部分階段可程式化」與「CUDA 推出」分別是哪一年,並寫出 CUDA 的全名。
2003 年繪圖 pipeline 部分階段變得完全可程式化(仍限繪圖);2006 年 NVIDIA 推出 CUDA,讓任意運算不經繪圖 API 即可使用 GPU throughput。
CUDA 全名為 Compute Unified Device Architecture。
Question 2 - Host / Device 與 Kernel [recall]
在 CUDA 的異質系統中,host 與 device 各指什麼?「kernel」與「launch a kernel」又是什麼意思?
host = CPU + host memory;device = GPU + device memory;應用程式永遠從 CPU(host)開始執行。
kernel 是被呼叫到 GPU 上執行的函式;launch kernel 指在 GPU 上平行啟動大量 threads 執行同一份 kernel code。
Question 3 - GPU DRAM 命名與 on-chip 記憶體粒度 [recall]
從 device code 的視角,連到 GPU 的 DRAM 叫什麼名稱?register file 與 shared memory 的配置粒度(per-thread 或 per-block)各為何?
連到 GPU 的 DRAM 稱為 global memory(名稱僅表示 GPU 內所有 SM 皆可存取,非系統各處可存取)。
register file 為 per-thread(存 thread 區域變數,由 compiler 配置);shared memory 為 per-block(供同一 thread block / cluster 內 threads 交換資料)。
Question 4 - Array 與 Tile 的對比 [recall]
在 tile programming model 中,array 與 tile 在「存放位置」「可變性」「能否當 kernel 參數」三方面有何不同?
Array(global array):存於 device memory、mutable(可被 store 修改)、可當 kernel 參數。
Tile:只存在於 tile code、block 區域、immutable(每次運算產生新 tile)、不可當 kernel 參數;且每個維度須為 2 的次方且編譯期已知。
Question 5 - Compute Capability、PTX 與 cubin 命名 [recall]
Compute Capability 與 SM 版本如何對應(以 CC 12.0 為例)?
compute_80與sm_86分別指哪一種產物?
CC 採 X.Y 格式並直接對應 SM 版本:CC 12.0 → sm_120,SM 版本用來標記 binary 的編譯目標。
compute_80 指 PTX(虛擬 ISA,對應 CC 8.0);sm_86 指 cubin(實體 GPU 二進位,對應 CC 8.6)。
Question 6 - Warp 與 SIMT [recall]
一個 warp 由幾個 threads 組成、lane 如何編號?SIMT 的全名是什麼?什麼情況稱為 warp divergence、會造成什麼後果?
一個 warp 由 32 個 threads 組成,lane 編號 0–31。SIMT 全名為 Single-Instruction Multiple-Threads。
當同一 warp 內不同 threads 走不同控制流路徑時稱 warp divergence,未走該分支的 lane 會被 mask off(閒置),使利用率下降;warp 內走相同控制流時利用率最高。
Question 7 - 選擇 thread block 大小 [application]
你要為一筆 1000 個元素的工作 launch 一個 SIMT kernel,打算用每個 block 1000 個 threads。從 warp 角度看這個數字好嗎?該如何調整較佳?
不理想:thread block 的 thread 數最好是 32 的倍數,1000 不是(1000 / 32 = 31 餘 8),最後一個 warp 會有部分 lane 全程閒置,導致 functional units 利用率與記憶體存取次佳(suboptimal)。
較佳做法是選 32 的倍數(如 256 或 1024)作為 block 大小,並讓 grid 涵蓋足夠 blocks 處理全部 1000 個元素(邊界以條件判斷保護)。
Question 8 - cubin 二進位相容性 [application]
某函式庫只內含為 CC 8.6(
sm_86) 編譯的 cubin。請判斷它能否直接載入並執行於 CC 8.0、CC 8.9、CC 9.0 的 GPU,並說明理由。
規則:同一 major 版本內,minor CC >= 目標者可載入;跨 major 不相容。
CC 8.9 可以(同 major、minor 9 ≥ 6);CC 8.0 不可(minor 0 < 6);CC 9.0 不可(跨 major 8→9)。若要支援更舊或跨 major 裝置,可改內嵌 PTX 靠 JIT forward compatibility。
Question 9 - 為何 block 間不可有資料相依 [analysis]
CUDA 要求 grid 內不同 thread block 之間無排程順序保證、且彼此無資料相依、必須可任意順序執行。為什麼這條限制對 GPU 的可擴展性很重要?
每個 block 由單一 SM 執行且通常跑到完成,但 GPU 的 SM 數量從 1 個到數千個不等,且 block 被分配到可用 SM 的順序不固定。
若 block 間可任意順序(平行或串行)執行,同一份任意大小的 grid 就能在任意大小的 GPU 上正確執行而無需改寫;一旦允許跨 block 資料相依,就會因排程順序不定而產生死結或錯誤結果。跨 block 同步須改用 cluster(CC 9.0+)等受支援機制。
Question 10 - Mapped memory 為何不是高效替代 [analysis]
有人想用 mapped memory(可被 GPU 直接存取的 CPU memory)取代 unified memory 或「把資料放到適當記憶體空間」。從效能角度分析為何這通常不是好主意。
mapped memory 的 GPU 存取走 PCIe 或 NVLINK 連線,其延遲較高、頻寬較低,而且 GPU 無法用 parallelism 隱藏這種延遲,因此不是 unified memory 或正確資料放置的高效替代方案。
較佳策略是把資料放在直連存取它的處理器(GPU 用 global memory、CPU 用 system memory),即使用 unified memory 也應盡量減少跨記憶體搬移以取得最佳效能。
| 主題 | 必記重點 |
|---|---|
| GPU 基礎 | 2003 = pipeline 部分可程式化;2006 = CUDA(Compute Unified Device Architecture);GPU 換 throughput、CPU 換 latency;上手 = libraries / frameworks / DSLs |
| 執行模型 | host=CPU、device=GPU、程式從 CPU 起;kernel = GPU 函式、launch = 平行起大量 threads;block 在單一 SM 跑到完成、block 間無順序保證且無資料相依 |
| SIMT / warp | warp = 32 threads、lane 0–31;SIMT = Single-Instruction Multiple-Threads;divergence → lane 被 mask;block thread 數取 32 的倍數 |
| Tile | per-block 寫程式、compiler 決定 threads/block、無 warp divergence;array mutable / tile immutable;tile 維度為 2 的次方且編譯期已知、不可當 kernel 參數;load 越界補零、store 越界丟棄 |
| 記憶體階層 | global memory(GPU DRAM);register per-thread、shared memory per-block;L1 per-SM、L2 全 GPU 共享;unified memory 可 CPU/GPU 存取,mapped memory 走 PCIe/NVLINK 非高效替代 |
| CUDA 平台 | CC X.Y 直接對應 SM 版本(CC 12.0 → sm_120);compute_XY = PTX、sm_XY = cubin;binary 同 major minor >= 可載入、跨 major 不相容;PTX JIT 到相同或更高 CC(forward compat),由 device driver 執行、快取於 compute cache |