GPU/CPU 内存层级与数据传输
阅读时间: 约 15 分钟
前置要求: vLLM 架构概览
理解硬件内存层级和数据传输特性是优化 LLM 推理的关键。本文介绍 GPU/CPU 内存架构以及 UCM 如何利用这些特性进行优化。
1. 内存层级金字塔#
1.1 层级结构#
graph TB
subgraph pyramid["内存层级金字塔"]
L1["GPU 寄存器/L1 Cache
最快 ~1ns | 最小 ~KB"]
L2["GPU L2 Cache
快 ~10ns | 小 ~MB"]
HBM["GPU HBM (显存)
中 ~300ns | 中 40-80GB"]
CPU["CPU DRAM (内存)
慢 ~100ns | 大 100GB-TB"]
SSD["NVMe SSD
很慢 ~10μs | 很大 TB级"]
NET["网络存储
最慢 ~ms | 最大 PB级"]
L1 --> L2
L2 --> HBM
HBM --> CPU
CPU --> SSD
SSD --> NET
end
subgraph properties["特性"]
P1["越往上: 越快、越贵、越小"]
P2["越往下: 越慢、越便宜、越大"]
end
1.2 各层级参数对比#
| 存储层级 |
延迟 |
带宽 |
典型容量 |
每 GB 成本 |
| GPU 寄存器 |
~1 ns |
~TB/s |
~KB |
- |
| GPU L1 Cache |
~5 ns |
~10 TB/s |
~128 KB |
- |
| GPU L2 Cache |
~30 ns |
~5 TB/s |
~50 MB |
- |
| GPU HBM |
~300 ns |
~2 TB/s |
40-80 GB |
$$$ |
| CPU DRAM |
~100 ns |
~200 GB/s |
100 GB - 1 TB |
$$ |
| NVMe SSD |
~10 μs |
~7 GB/s |
1-30 TB |
$ |
| 网络存储 |
~1 ms |
~10 GB/s |
PB 级 |
¢ |
2. GPU 显存(HBM)#
2.1 HBM 特性#
HBM (High Bandwidth Memory) 是现代 GPU 的主要内存:
graph TB
subgraph hbm["GPU HBM 特性"]
subgraph advantages["优势"]
A1["高带宽
A100: 2 TB/s
H100: 3.35 TB/s"]
A2["低延迟
~300ns"]
A3["直接访问
GPU 计算单元直接访问"]
end
subgraph limitations["限制"]
L1["容量有限
40-80 GB"]
L2["成本高
占 GPU 成本大比例"]
L3["独占
每块 GPU 独立"]
end
end
2.2 HBM 使用分配#
以 A100 80GB 为例:
pie title "A100 80GB HBM 使用分配(Llama-70B 示例)"
"模型权重" : 35
"KV Cache" : 30
"激活值" : 10
"CUDA 运行时" : 3
"预留/碎片" : 2
关键洞察: KV Cache 占用了大量显存,是优化的重点目标。
3. CPU 内存与 Pinned Memory#
3.1 普通内存 vs Pinned Memory#
graph TB
subgraph normal["普通内存(Pageable)"]
N1["可以被操作系统换出"]
N2["GPU 传输需要额外复制"]
N3["传输路径长"]
end
subgraph pinned["Pinned Memory(固定内存)"]
P1["锁定在物理内存"]
P2["DMA 直接访问"]
P3["传输速度更快"]
end
subgraph comparison["传输对比"]
C1["普通内存 → GPU
CPU→临时缓冲→GPU
~10 GB/s"]
C2["Pinned Memory → GPU
CPU→GPU 直接DMA
~25 GB/s"]
end
normal --> C1
pinned --> C2
3.2 UCM 的 Pinned Memory 使用#
UCM 使用 Pinned Memory 缓冲池加速数据传输:
# UCM Pinned Memory 缓冲池示意
class PinnedMemoryPool:
def __init__(self, buffer_count=1024, buffer_size=4096):
# 预分配固定数量的 Pinned Memory 缓冲区
self.buffers = [
torch.empty(buffer_size, dtype=torch.uint8, pin_memory=True)
for _ in range(buffer_count)
]
def get_buffer(self):
# 从池中获取可用缓冲区
return self.free_buffers.pop()
def return_buffer(self, buffer):
# 归还缓冲区到池
self.free_buffers.append(buffer)
4. 数据传输路径#
4.1 PCIe 传输#
CPU 和 GPU 之间通过 PCIe 总线传输数据:
graph LR
subgraph cpu["CPU 侧"]
DRAM["CPU DRAM"]
PCIE_C["PCIe Controller"]
end
subgraph pcie["PCIe 总线"]
BUS["PCIe Gen4/5
32/64 GB/s"]
end
subgraph gpu["GPU 侧"]
PCIE_G["PCIe Controller"]
HBM["GPU HBM"]
end
DRAM --> PCIE_C
PCIE_C --> BUS
BUS --> PCIE_G
PCIE_G --> HBM
4.2 NVLink(多 GPU)#
NVLink 提供 GPU 之间的高速直连:
graph TB
subgraph nvlink["NVLink 连接"]
GPU1["GPU 0"] <--> |"NVLink
600 GB/s"| GPU2["GPU 1"]
GPU2 <--> |"NVLink"| GPU3["GPU 2"]
GPU3 <--> |"NVLink"| GPU4["GPU 3"]
GPU4 <--> |"NVLink"| GPU1
end
subgraph pcie["对比 PCIe"]
P["PCIe Gen4
32 GB/s"]
end
4.3 传输带宽对比#
| 传输路径 |
带宽 |
延迟 |
| GPU 内部 (HBM) |
~2000 GB/s |
~300 ns |
| GPU-GPU (NVLink) |
~600 GB/s |
~1 μs |
| GPU-CPU (PCIe 4.0) |
~32 GB/s |
~10 μs |
| CPU-SSD (NVMe) |
~7 GB/s |
~10 μs |
| CPU-Network |
~10 GB/s |
~1 ms |
5. 异步传输与重叠#
5.1 同步 vs 异步传输#
sequenceDiagram
participant CPU
participant GPU
Note over CPU,GPU: 同步传输 - 阻塞等待
CPU->>GPU: 发起传输
CPU->>CPU: 等待完成...
GPU-->>CPU: 传输完成
CPU->>CPU: 继续执行
Note over CPU,GPU: 异步传输 - 并行执行
CPU->>GPU: 发起传输(非阻塞)
CPU->>CPU: 继续其他工作
GPU-->>CPU: 传输完成通知
5.2 计算与传输重叠#
UCM 利用异步传输实现计算与数据传输的重叠:
gantt
title 计算与传输重叠示意
dateFormat X
axisFormat %s
section 无重叠
加载 KV :load1, 0, 3
计算 Attention :compute1, 3, 5
保存 KV :save1, 5, 8
section 有重叠
加载 KV (Layer N+1) :load2, 0, 3
计算 Attention (Layer N) :compute2, 0, 2
保存 KV (Layer N-1) :save2, 0, 2
5.3 CUDA Streams#
CUDA Streams 用于管理异步操作:
class AsyncTransfer:
def __init__(self):
self.compute_stream = torch.cuda.Stream()
self.transfer_stream = torch.cuda.Stream()
def overlapped_execution(self, kv_data, compute_fn):
# 在 transfer stream 上异步加载
with torch.cuda.stream(self.transfer_stream):
kv_gpu = kv_data.to('cuda', non_blocking=True)
# 在 compute stream 上执行计算
with torch.cuda.stream(self.compute_stream):
# 等待传输完成
self.compute_stream.wait_stream(self.transfer_stream)
result = compute_fn(kv_gpu)
return result
6. UCM 多级存储架构#
6.1 存储层级设计#
UCM 利用多级存储层级优化 KV Cache 管理:
graph TB
subgraph ucm["UCM 多级存储"]
subgraph l1["L1 - GPU HBM"]
H1["热点 KV Cache
最常访问的 Block"]
end
subgraph l2["L2 - CPU Pinned Memory"]
H2["温数据
Cache Store 缓存"]
end
subgraph l3["L3 - 本地存储"]
H3["持久化 KV
POSIX Store"]
end
subgraph l4["L4 - 分布式存储"]
H4["共享 KV
NFS/DS3FS/Mooncake"]
end
l1 --> |"淘汰"| l2
l2 --> |"淘汰"| l3
l3 --> |"归档"| l4
l4 --> |"预取"| l3
l3 --> |"加载"| l2
l2 --> |"热身"| l1
end
6.2 Pipeline Store 组合#
UCM 支持灵活的存储层级组合:
配置示例:
1. Cache|Posix - 本地高速缓存
HBM ↔ Pinned Memory Cache ↔ 本地 SSD
2. Cache|NFS - 分布式缓存
HBM ↔ Pinned Memory Cache ↔ NFS 共享存储
3. Cache|DS3FS - 云存储缓存
HBM ↔ Pinned Memory Cache ↔ S3 对象存储
7. 传输优化技术#
7.1 批量传输#
将多个小传输合并为大传输:
graph TB
subgraph nobatch["无批量 - 多次小传输"]
T1["传输 Block 1
4KB"] --> T2["传输 Block 2
4KB"]
T2 --> T3["传输 Block 3
4KB"]
T3 --> T4["传输 Block 4
4KB"]
end
subgraph batch["批量 - 一次大传输"]
B1["传输 Block 1-4
16KB 合并传输"]
end
nobatch --> |"4x 启动开销"| Result1["总时间长"]
batch --> |"1x 启动开销"| Result2["总时间短"]
7.2 预取(Prefetch)#
提前加载即将使用的数据:
sequenceDiagram
participant Sched as Scheduler
participant Prefetch as Prefetch Engine
participant Store as Storage
participant Worker as Worker
Sched->>Prefetch: 预测下一批请求
Prefetch->>Store: 提前加载 KV
Store-->>Prefetch: KV 数据
Prefetch->>Prefetch: 缓存到 GPU
Note over Worker: 稍后...
Worker->>Prefetch: 请求 KV
Prefetch-->>Worker: 立即返回(已预取)
7.3 零拷贝(Zero-Copy)#
减少不必要的数据复制:
graph TB
subgraph copy["有拷贝"]
S1["存储"] --> |"读取"| B1["缓冲区 1"]
B1 --> |"复制"| B2["缓冲区 2"]
B2 --> |"传输"| G1["GPU"]
end
subgraph zerocopy["零拷贝"]
S2["存储"] --> |"直接传输"| G2["GPU"]
end
8. 关键概念总结#
| 概念 |
说明 |
UCM 应用 |
| HBM |
GPU 高带宽显存 |
热点 KV 存储 |
| Pinned Memory |
CPU 固定内存 |
Cache Store 缓冲池 |
| PCIe |
CPU-GPU 传输总线 |
决定传输带宽上限 |
| 异步传输 |
非阻塞数据传输 |
计算传输重叠 |
| CUDA Streams |
异步操作管理 |
并行调度 |
| 预取 |
提前加载数据 |
Prefetch Engine |
| 多级存储 |
分层存储架构 |
Pipeline Store |
延伸阅读#