Host/Device 互動模型與記憶體演進 (Host/Device Interaction Model)
重點總覽 (Overview)
本節追蹤 CPU-GPU 記憶體模型 從「Host/Device 各自獨立、必須 cudaMemcpy() 搬資料」演進到「單一指標、自動遷移」的歷史。每一步都是為了解決早期簡單模型的兩大痛點:I/O 裝置只認 host memory 與 大型資料結構塞不進 device memory。
| 年代 / 版本 | 硬體世代 | 里程碑 | 解決的問題 | 關鍵 API / 數字 |
|---|---|---|---|---|
| 2009 / CUDA 2.2 | (pre-Fermi) | Zero-copy memory | kernel 直接讀 host pinned memory,免 cudaMemcpy() |
cudaHostAlloc(...,cudaHostAllocMapped)、cudaHostGetDevicePointer() |
| 2011 / CUDA 4 | Fermi | UVA (Unified Virtual Addressing) | host/device 共用單一虛擬位址空間,免指定複製方向 | 每個實體位址 ↔ 唯一虛擬位址 |
| 2013 / Kepler | Kepler | 大型位址空間 | 突破 32-bit / 4 GB 限制;GPU 間 peer access | 64-bit 虛擬、≥40-bit 實體 |
| 2013 / CUDA 6 | Kepler/Maxwell | Unified (managed) memory | 單一指標、自動 migration/coherence | cudaMallocManaged() |
| 2016 / Pascal | Pascal | Page fault handling + 49-bit VA | 免每次 launch flush;遍歷 host 上的指標結構 | 49-bit 虛擬位址 |
| CUDA 11 | (Ampere+) | Virtual address space control | 跨裝置自訂資料佈局、單一指標 | cuMemAddressReserve/Create/Map() |
核心趨勢:指標愈來愈「通用」。早期 host 指標與 device 指標是兩個世界 → UVA 讓兩者不重疊 → 大位址空間讓兩者「值相同」→ Unified Memory + page fault 讓同一指標在 CPU/GPU 上都能 dereference 並自動把資料搬到存取端。
Host/Device 簡單模型的限制 (Limitations of the Simple Model)
前面章節 (見 02-Heterogeneous-Data-Parallel-Computing/02-Vector-Addition-Host-Code-Device-Memory-and-Data-Transfer) 假設的模型:device memory (CUDA global memory) 與 host (system) memory 完全分離,必須用 cudaMemcpy() 來回搬資料。
傳統模型 (simple model)
Host Memory --cudaMemcpy(H2D)--> Device Memory
(system) <--cudaMemcpy(D2H)-- (GPU global)
| |
I/O 裝置 Kernel
(disk/NIC 只認 host memory)
兩大痛點:
- I/O 效率:disk controller、NIC 等 I/O 裝置設計成在 host memory 上運作。資料先 H2D 再運算、運算完 D2H 才能 I/O → 增加 I/O latency、降低 throughput。
- 大型資料結構:傳統程式把大資料結構放 host memory;早期 GPU device memory 很小,逼開發者把資料 切塊 (partition) 才塞得下。
- 例:18-Electrostatic-Potential-Map/01-DCS-Scatter-vs-Gather 的 3D 能量網格被切成 2D slices 來回搬運。
- 有些應用根本沒有好的切法 → 最好讓 GPU 直接存取 host memory,或由 runtime 自動遷移。
這些限制 根源於早期 GPU 的記憶體架構(小容量、32-bit 位址),不是 CUDA 程式語言本身的限制。後續演進主要靠硬體 + runtime 共同解決。
Zero-Copy Memory 與 UVA (Zero-Copy Memory and Unified Virtual Addressing)
Zero-Copy Memory (CUDA 2.2, 2009)
讓 kernel 直接透過 system interconnect (PCIe) 存取 host memory,完全不呼叫 cudaMemcpy()。
// 1) 配置 pinned + mapped 的 host memory
float *h_ptr;
cudaHostAlloc((void**)&h_ptr, size, cudaHostAllocMapped);
// 2) host 指標「不能」直接傳給 kernel,要先換成 device 指標
float *d_ptr;
cudaHostGetDevicePointer((void**)&d_ptr, h_ptr, 0);
// 3) 把 device 指標傳進 kernel;kernel deref 時走 PCIe 直接讀寫 host memory
myKernel<<<grid, block>>>(d_ptr);
- Zero-copy memory 必須是 pinned host memory (見 20-Heterogeneous-Computing-Cluster/03-Overlapping-Computation-and-Communication),避免 OS 在 GPU 存取時把頁面換出。
- 同一塊實體記憶體,host 與 device 用不同指標 (
h_ptrvsd_ptr) 存取。
h_ptr (host VA) d_ptr (device VA)
\ /
\ /
v v
┌─────────────────────────────────┐
│ 同一塊 pinned host 實體記憶體 │ <-- 兩個不同虛擬位址
└─────────────────────────────────┘
▲ PCIe 直接存取 (慢)
Kernel on GPU
System interconnect 頻寬 < global memory 頻寬的 10%。若 kernel 大量存取 zero-copy memory,速度會被 PCIe 嚴重拖慢。
規則:zero-copy memory 只適合 偶爾、稀疏存取 的資料結構。
Unified Virtual Addressing (UVA, CUDA 4, 2011)
- UVA 前:host 與 device 各有 虛擬位址空間 → 同一實體位址可能對應到 host/device 兩個不同虛擬位址 (zero-copy 即是如此)。
- UVA 後:單一共用虛擬位址空間 (UVAS),保證 每個實體位址只對應一個虛擬位址。
- runtime 只看指標值就能判斷它指向 host 還是 device →
cudaMemcpy()不再需要指定方向 (可用cudaMemcpyDefault)。 - 源自 GMAC library (Gelado et al., 2010)。
- runtime 只看指標值就能判斷它指向 host 還是 device →
| 項目 | UVA 之前 | UVA (CUDA 4) |
|---|---|---|
| 虛擬位址空間 | host / device 各一份 | 單一共用 (UVAS) |
| 實體↔虛擬映射 | 一個實體可對多個虛擬 | 每個實體 → 唯一虛擬 |
cudaMemcpy 方向 |
必須指定 (H2D/D2H/...) | 可自動判斷 |
UVA 不保證 accessibility。host code 仍不能直接 deref cudaMalloc() 回傳的 device 指標,反之亦然。例外:zero-copy 指標可直接當 kernel 參數。
此外,zero-copy 指標 不一定能跟著 deref 出來的指標鏈走 (traverse linked list),除非所有記憶體都用 cudaHostAlloc() 配置。
大型虛擬與實體位址空間 (Large Virtual and Physical Address Spaces)
32-bit 的天花板
早期 CUDA GPU 用 32-bit 虛擬位址 + 最多 32-bit 實體位址:
- device memory 上限 4 GB;CUDA kernel 只能操作 < 4 GB 的資料集。
- 現代 CPU 用 64-bit 虛擬位址 (實際用 48 bits) → GPU 的 32-bit 虛擬位址 裝不下 CPU 位址,這也限制了 zero-copy 能支援的資料結構類型。
Kepler (2013) 起的現代虛擬記憶體
| 位址 | 早期 GPU | Kepler+ | 現代 CPU |
|---|---|---|---|
| 虛擬位址 | 32-bit | 64-bit | 64-bit (用 48) |
| 實體位址 | ≤32-bit (4 GB) | ≥40-bit (>4 GB DRAM) | — |
好處:
- GPU 可裝 >4 GB DRAM;kernel 可操作大型資料集。
- host 與 device 可用 完全相同的指標值 存取同一份資料 (不論在哪)。
- 跨 GPU 統一實體位址空間 → 一顆 GPU 可直接 deref 指向另一顆 GPU memory 的指標 (peer access)。
Kepler 之前 (GPU 間溝通): Kepler 之後 (直接 peer access):
GPU0 mem --D2D copy(host觸發)--> GPU1 GPU0 直接 deref --> GPU1 mem
(額外記憶體 + copy overhead) (指標傳過去即可,如 halo exchange)
這解決了 20-Heterogeneous-Computing-Cluster/04-Collective-Communication-and-CUDA-Aware-MPI 中 stencil halo exchange 早期須靠 host 觸發 device-to-device copy 的開銷問題。
統一記憶體 (Unified Memory)
Managed Memory (CUDA 6, 2013)
建立一個 CPU/GPU 共享的 managed memory pool,用 單一指標 存取。變數可駐留在 CPU 實體記憶體、GPU 實體記憶體、或兩者都有;runtime + 硬體負責 data migration 與 coherence。
移植成本極低 (Fig 22.1):CPU code 改兩處即可變 CUDA:
// CPU 版 // Unified Memory CUDA 版
int *data = (int*)malloc(N*4); cudaMallocManaged(&data, N*4); // 改 1
// ...fill data... // ...fill data... (CPU 直接寫)
qsort(data, N, 4, cmp); sortKernel<<<g,b>>>(data, N); // 改 2
cudaDeviceSynchronize();
free(data); cudaFree(data);
Unified memory 不會 自動處理並行存取正確性。CPU/GPU 同時存取同一塊 managed memory 時,仍須自行用 barrier / atomic operations 來同步 (見 09-Parallel-Histogram/01-Atomic-Operations-and-Basic-Histogram)。
Kepler/Maxwell 的限制 (軟體為主的實作)
- 每次 grid launch 前,CPU 改過的 managed memory 全部要 flush 到 GPU device memory。
- CPU 與 GPU 不能同時 存取同一塊 managed allocation。
- unified address space 受限於 GPU 實體記憶體大小。
- 原因:這些世代 缺乏 host/device 之間的硬體 coherence,migration 多由軟體完成。
Pascal (2016):Page Fault Handling + 49-bit VA
兩大硬體特性:
- 49-bit 虛擬位址 → 足以涵蓋現代 CPU 的 48-bit VA + GPU 自身記憶體 → 整個系統 (所有 CPU/GPU) 成為 單一虛擬位址空間,不再受限於「能 copy 到 device 的資料量」。
- Page fault handling:
Pascal 之前:launch 前 flush 全部 managed memory (粗暴、慢)
Pascal 之後:on-demand,靠 page fault + page protection 做 coherence
GPU grid 存取某 page
|
v
page 不在 GPU / 已被 CPU invalidate?
|── 是 ──> 觸發 page fault
| ├─ migrate page 到 GPU memory (按需),或
| └─ map on access 走 interconnect (偶爾存取時更快)
└── 否 ──> 直接存取
- coherence 機制:host/device 修改 managed 變數時,用 page mapping/protection 讓對方的副本失效 (invalidate);launch 時 不必 把所有 GPU 副本更新到最新。
- system-wide:GPU/CPU 可從 CPU memory 或 其他 GPU memory fault + migrate page。
- CPU deref 指向 GPU memory 的指標也能被服務 (latency 較長) → 方便呼叫 尚未移植到 GPU 的 legacy library。
Pascal 起,GPU 可 遍歷 host memory 中的 linked data structure,即使資料不在 zero-copy memory:因為 host 與 device 用同一指標值 指同一變數,host 建的指標鏈 device 也能走 (反之亦然)。對 CAD 等需要數百 GB「in core」資料集的應用特別關鍵。
| 能力 | Zero-copy (2.2) | Unified Memory (Pascal) |
|---|---|---|
| 存取方式 | PCIe 直接讀,無遷移 | 按需 migrate / map-on-access |
| 遍歷指標鏈 | 只限全部 cudaHostAlloc 配置 |
任意 host 建立的結構皆可 |
| 資料量上限 | host pinned memory | 全系統 (CPU + 多 GPU) VA |
| coherence | 無 (手動) | 硬體 + page fault 自動 |
虛擬位址空間控制 (Virtual Address Space Control)
CUDA 11 提供一組 low-level API 讓程式設計師精細控制記憶體配置:
cuMemAddressReserve(&ptr, size, ...); // 1. 保留一段虛擬位址範圍
cuMemCreate(&handle, size, &prop, 0); // 2. 在任一 device 配置實體記憶體
cuMemMap(ptr + offset, size, 0, handle, 0); // 3. 映射到保留範圍的任一位置
- 可建立 跨多裝置的自訂資料佈局。
- 例:把一個 3D volume 配置橫跨多顆 GPU,卻仍用 單一指標 來引用整個 volume。
考試/面試重點 (Exam / Test Patterns)
| 情境 / 關鍵字 | 答案 / 技巧 |
|---|---|
| zero-copy memory 怎麼配置? | cudaHostAlloc(..., cudaHostAllocMapped),再 cudaHostGetDevicePointer() 取 device 指標 |
| zero-copy 何時用? | 只用於 偶爾、稀疏存取 的資料;頻寬只有 global memory 的 <10% |
| 為何 zero-copy 要 pinned memory? | 防止 OS 在 GPU 透過 PCIe 存取時把頁面 page out |
| UVA 解決什麼? | 單一虛擬位址空間,每個實體位址唯一映射 → cudaMemcpy 免指定方向 |
| UVA 保證能存取嗎? | 不。host 仍不能 deref cudaMalloc 指標 (zero-copy 例外) |
| 32-bit GPU 的記憶體上限? | |
| Kepler 帶來什麼位址改變? | 64-bit 虛擬、≥40-bit 實體 → >4 GB DRAM + GPU 間 peer access |
| Pascal 的虛擬位址寬度 + 新功能? | 49-bit VA + page fault handling |
| Unified Memory 兩行移植 (Fig 22.1)? | malloc/free → cudaMallocManaged/cudaFree;qsort → kernel + cudaDeviceSynchronize |
| Kepler/Maxwell unified memory 限制? | launch 前須 flush、CPU/GPU 不能同時存取、受限 GPU 實體記憶體大小 |
| Pascal 如何免 flush? | page fault + page protection 做 invalidate-based coherence,on-demand migrate |
| GPU 遍歷 host linked list 需要? | Pascal 起 + 同一指標值 (system-wide VA);不必放 zero-copy |
| CUDA 11 low-level VA API? | cuMemAddressReserve / cuMemCreate / cuMemMap |
| managed memory 並行存取正確性? | runtime 不保證,須自行 barrier / atomic |
Related Notes
- 22-Advanced-Practices-And-Future-Evolution/02-Kernel-Execution-Control
- 22-Advanced-Practices-And-Future-Evolution/03-Memory-Bandwidth-and-Compute-Throughput
- 22-Advanced-Practices-And-Future-Evolution/04-Programming-Environment-and-Future-Outlook
- 02-Heterogeneous-Data-Parallel-Computing/02-Vector-Addition-Host-Code-Device-Memory-and-Data-Transfer
- 05-Memory-Architecture-And-Data-Locality/01-Memory-Access-Efficiency-and-CUDA-Memory-Types
- 20-Heterogeneous-Computing-Cluster/03-Overlapping-Computation-and-Communication