GPU 解剖与 LLM Token 数据流

要理解 LLM 推理为什么慢、瓶颈在哪,必须先看清一个 token 在 GPU 硬件里走过的完整路径。本文从 H100 芯片全貌、SM 内部结构、内存层级,一直讲到一个 token 从输入到输出的数据流动,配有可交互图示。

Part 1: GPU 芯片全貌

H100 SXM 是 814mm² 的巨型芯片,包含 132 个 SM、80GB HBM3、3.35 TB/s 带宽。下面是一张可交互的 GPU die 示意图,点击任意组件(HBM、Memory Controller、L2 Cache、Crossbar、SM、NVLink)查看详细说明:

Part 2: SM(流处理器)内部

每个 SM 是 GPU 的基本计算单元,里面包含 Tensor Cores、CUDA Cores、SRAM 和调度器。

Warp Scheduler × 4

每个 SM 有 4 个 Warp 调度器,每个管理多个 Warp(32 线程为一组)。当一个 Warp 等待内存时,调度器立刻切换到另一个就绪的 Warp(延迟隐藏)。每 SM 最多同时管理 64 个 Warp = 2048 个线程。

Tensor Cores × 4 (4th Gen)

专为矩阵乘法设计的硬件单元。每个 Tensor Core 每周期可算一个 4×4 矩阵 FMA,支持 FP16、BF16、FP8、INT8 格式。**LLM 中几乎所有 Linear 层都在这里算。**输入从 Register File 读取,结果写回 Register。

CUDA Cores (FP32) × 128

通用浮点运算单元(ALU)。处理逐元素操作:加法、RMSNorm、GeLU/SiLU 激活、softmax、RoPE 位置编码等。每周期 1 个 FP32 FMA / core。比 Tensor Core 灵活但慢得多(做矩阵乘法效率极低)。

Shared Memory / L1 (SRAM) — 228KB

SM 内的高速暂存区,程序员可显式管理。Flash Attention 的核心:把 K、V 分块加载到这里,在 SRAM 中完成 + softmax + ,避免写回 HBM。带宽 ~30 TB/s(是 HBM 的 9×),延迟 ~30 cycle。大小是主要限制 —— 只有 228KB,放不下整个 KV Cache。

Register File — 256KB

每个线程的私有超高速存储。Tensor Core 和 CUDA Core 直接从这里读/写操作数。延迟 ~1 cycle,带宽 ~60 TB/s。每线程最多 255 个 32-bit 寄存器。寄存器压力:用太多 → 能同时跑的 Warp 数变少 → 延迟隐藏效果变差。

Load/Store Units × 32

负责所有内存读写操作。每次 memory request 经过:(1) 先查 L1/Shared Memory;(2) Miss → 查 L2 Cache;(3) Miss → 去 HBM。32 个 LD/ST unit 意味着每周期可发起 32 个 memory transaction。LLM decode 时,大部分时间都在等这些 unit 把权重从 HBM 搬进来。

三个关键直觉

Tensor Core vs CUDA Core:Tensor Core 专做 D = A×B + C 的矩阵 FMA,一拍算 4×4 矩阵;CUDA Core 一拍算 1 个标量乘加。做一个 4×4 矩阵乘法,Tensor Core 1 个周期,CUDA Cores 需要 64 次乘 + 48 次加。所以 LLM 的 Linear 层全用 Tensor Core,逐元素操作用 CUDA Core。

为什么 SRAM 这么重要:HBM 是 80GB 但只有 3.35TB/s;SRAM 每 SM 只有 228KB 但有 ~30TB/s。Flash Attention 的秘密就是把数据尽量留在 SRAM 中计算,避免来回读写 HBM。朴素 Attention 把 结果写回 HBM 再读回做 softmax;Flash 则分块全在 SRAM 中完成,HBM 只读一次 K、V。

Warp 和延迟隐藏:HBM 读取延迟 ~400ns ≈ 数百个时钟周期。如果只有 1 个线程组在跑,GPU 大部分时间在等内存。解决办法是每 SM 放 64 个 Warp,当一个 Warp 等内存时调度器瞬间切换到另一个就绪的 Warp。这就是 GPU 的核心设计哲学 —— 用并发换延迟

Part 3: 内存层级

从最远的 HBM 到最近的 Register,越近越快但越小:

层级 容量 带宽 延迟 存放内容
HBM3(显存) 80 GB 3.35 TB/s ~400 ns 模型权重 + KV Cache + 激活值 + 优化器状态
L2 Cache 50 MB ~12 TB/s ~150 ns 所有 SM 共享,缓存 RoPE 表、小权重、频繁访问的 KV
Shared Memory / L1 (SRAM) 228 KB / SM ~30 TB/s ~30 ns SM 私有,程序员管理,Flash Attention 工作区
Register File 256 KB / SM ~60 TB/s ~1 ns 线程私有,计算单元直接操作数来源
Tensor + CUDA Cores 989 TFLOPS FP16 / ~67 TFLOPS FP32 从 Register 读操作数 → 计算 → 写回 Register

**关键矛盾:**Tensor Cores 需要 989 TFLOPS 的「喂数据」速度,但 HBM 只能提供 3.35 TB/s。

这就是 H100 的算力带宽比(屋脊线)。LLM Decode 的 GEMV 只有 ~1-2 FLOPs/Byte —— Tensor Cores 99% 的时间在空等 HBM!

Part 4: Token 数据流动

下面这个可交互图示,逐步演示一个 token 从输入到输出在 GPU 硬件中的完整旅程(LLaMA-70B Decode)。每一步会高亮活跃的 HBM 区域、SM 和缓存,并说明数据从哪里来、到哪里去、经过什么计算单元:

整个流程的关键观察:

  • **Step 2 / 5 / 8 / 10(各种 Projection)**都是 GEMV:权重远大于激活值,从 HBM 流式读取权重 → Tensor Cores 计算,是 memory-bound 的根源。
  • **Step 4(Attention / Flash Decoding)**全程 score 矩阵不写回 HBM,只读一次 KV、输出一个向量。
  • **Step 7(FFN Gate + Up)**是整个推理过程中最大的一次权重读取:W_gate + W_up = 896MB。

一层的总权重读取约 1.86GB(Attention 512MB + FFN 1.34GB)。70B 模型 80 层,每生成一个 token 需要读 ~148GB 权重 + ~1.3GB KV Cache ≈ 150GB。理论耗时:

实际约 55-70ms(各种 overhead)。最终输出只是 1 个整数(next_token_id) —— 这就是你看到的「一个字一个字冒出来」。然后这个 token 重新进入 Step 0,开始下一轮 decode。


本文正文 markdown 渲染,2 个交互图示通过自定义 {% anim %} 标签以隔离 iframe 嵌入,源自 Arkive 教程。