先給一句話:CUDA 是 NVIDIA 推出的「用 C/C++/Python 等語言操控顯示卡做平行計算」的工具與生態系。
它讓你把原本在 CPU 上慢慢做的工作,搬到擁有成千上萬「小工人」的 GPU 上,同時做完。
當你的任務是:
流程長這樣(概念示意):
CPU(Host) 準備資料 → 傳到 GPU(Device)
↓
啟動 Kernel(一次啟動許多執行緒)
↓
GPU 算完 → 把結果傳回 CPU
關鍵心法:Host 安排工作、Device 真的動手做。
CPU 常用 SIMD(Single Instruction, Multiple Data):一個指令同時處理多筆資料(像 AVX)。
CUDA 讓你寫的程式邏輯像是每個執行緒在跑自己的指令,但底層實作是 SIMT(Single Instruction, Multiple Threads):
if
的「左邊路」,有人走「右邊路」,就會 分歧(Divergence),造成隊伍分開排隊,速度下降。口訣:讓同一個 Warp 內的執行緒走相同路線,就會跑得快。
當你啟動一個 Kernel,會一次啟動非常多的執行緒。CUDA 用三層來管理:
Grid(整個任務)
└─ 多個 Block(中隊)
└─ Block 內有很多 Thread(士兵)
示意圖(1D 版本):
Grid
├─ Block 0: [Thread 0 ... Thread 255]
├─ Block 1: [Thread 256 ... Thread 511]
└─ Block 2: [Thread 512 ... Thread 767]
核心觀念:Block 是協作單位;同一 Block 內才能共享某些快取資源與同步。不同 Block 之間不能彼此直接同步。
當有些 Warp 在等資料(例如等記憶體讀取),SM 就讓其他 Warp 先上,這叫「延遲隱藏(Latency Hiding)」。
為了隱藏延遲,你需要足夠多的活躍執行緒(稱為 Occupancy 載入度)來填滿 SM。
GPU 有多層記憶體,速度與容量差很多:
心法:
- 先想如何少讀 Global Memory。
- 能把重複用的資料放進 Shared Memory 就放。
- 讀取時盡量讓連續執行緒讀連續位址(合併存取 / Coalesced Access),速度會差很多。
__syncthreads()
的概念)一起等齊再繼續,並共享 Shared Memory。設計演算法時,要把「需要頻繁交換資料的工作」盡量安排在同一個 Block 裡完成。
PCIe 傳輸:Host(CPU)與 Device(GPU)之間的資料要走 PCIe,這步可能很慢。
Unified Memory(統一記憶體):讓你不必手動管理「拷貝到 GPU / 拷回 CPU」這件事,由系統在需要時搬運。
共通特徵:大量、規則、可平行的工作,拆成很多小任務同時做。
資料:
分工:
分歧(Divergence):
if
走不同路,會排隊分時執行,變慢;任務是否大量、可平行、重複?
├─ 否:CPU(或混合)
└─ 是:
Host(準備資料/啟動Kernel) → Device(GPU執行)
├─ 設計 Grid/Block/Thread 分工
├─ 最小化 PCIe 搬運
├─ Shared Memory 作快取
├─ 合併存取(Coalesced)
└─ 避免 Warp 分歧、提升 Occupancy
Q1:我只會 Python,可以用 CUDA 嗎?
可以。用 Numba(@cuda.jit)、CuPy(NumPy 類介面)、或直接用 PyTorch/TensorFlow 的 GPU 張量運算。
Q2:所有程式搬到 GPU 都會快嗎?
不一定。資料太小、流程分支很多、要頻繁 CPU↔GPU 搬運,可能更慢。GPU 適合「大量、規則、可平行」。
Q3:同一張卡打電動 + 跑 CUDA 會衝突嗎?
可能互搶資源(尤其是顯示畫面會吃時間),但能共存。工作站/伺服器通常分開使用。
Q4:我需要懂很多硬體細節嗎?
入門不用。先理解 Grid/Block/Thread、記憶體層級、分歧與合併存取,就足夠寫出初階高效的 Kernel。
CUDA = 把大量、規則、可拆分的工作交給 GPU 的方法與生態。
記住四個要點:大量平行、合併存取、善用 Shared Memory、避免分歧。掌握這些,你就具備了把現實問題「GPU 化」的最小可行能力。下一篇我們再把環境裝起來,跑出你的第一個 CUDA 程式!