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()
Important

核心趨勢:指標愈來愈「通用」。早期 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)

兩大痛點:

Warning

這些限制 根源於早期 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);
        h_ptr (host VA)                d_ptr (device VA)
            \                            /
             \                          /
              v                        v
        ┌─────────────────────────────────┐
        │  同一塊 pinned host 實體記憶體      │  <-- 兩個不同虛擬位址
        └─────────────────────────────────┘
                 ▲ PCIe 直接存取 (慢)
              Kernel on GPU
Warning

System interconnect 頻寬 < global memory 頻寬的 10%。若 kernel 大量存取 zero-copy memory,速度會被 PCIe 嚴重拖慢。
規則:zero-copy memory 只適合 偶爾、稀疏存取 的資料結構。

Unified Virtual Addressing (UVA, CUDA 4, 2011)

項目 UVA 之前 UVA (CUDA 4)
虛擬位址空間 host / device 各一份 單一共用 (UVAS)
實體↔虛擬映射 一個實體可對多個虛擬 每個實體 → 唯一虛擬
cudaMemcpy 方向 必須指定 (H2D/D2H/...) 可自動判斷
Warning

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 實體位址:

可定址記憶體=232=4 GB

Kepler (2013) 起的現代虛擬記憶體

位址 早期 GPU Kepler+ 現代 CPU
虛擬位址 32-bit 64-bit 64-bit (用 48)
實體位址 ≤32-bit (4 GB) ≥40-bit (>4 GB DRAM)

好處:

Kepler 之前 (GPU 間溝通):           Kepler 之後 (直接 peer access):
  GPU0 mem --D2D copy(host觸發)--> GPU1   GPU0 直接 deref --> GPU1 mem
    (額外記憶體 + copy overhead)            (指標傳過去即可,如 halo exchange)
Tip

這解決了 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);
Important

Unified memory 不會 自動處理並行存取正確性。CPU/GPU 同時存取同一塊 managed memory 時,仍須自行用 barrier / atomic operations 來同步 (見 09-Parallel-Histogram/01-Atomic-Operations-and-Basic-Histogram)。

Kepler/Maxwell 的限制 (軟體為主的實作)

Pascal (2016):Page Fault Handling + 49-bit VA

兩大硬體特性:

  1. 49-bit 虛擬位址 → 足以涵蓋現代 CPU 的 48-bit VA + GPU 自身記憶體 → 整個系統 (所有 CPU/GPU) 成為 單一虛擬位址空間,不再受限於「能 copy 到 device 的資料量」。
  2. 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 (偶爾存取時更快)
        └── 否 ──> 直接存取
Tip

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. 映射到保留範圍的任一位置

考試/面試重點 (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 的記憶體上限? 232 = 4 GB
Kepler 帶來什麼位址改變? 64-bit 虛擬、≥40-bit 實體 → >4 GB DRAM + GPU 間 peer access
Pascal 的虛擬位址寬度 + 新功能? 49-bit VA + page fault handling
Unified Memory 兩行移植 (Fig 22.1)? malloc/freecudaMallocManaged/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