✏️ 重新学习 LLM 01:大模型推理的工作原理、推理速度及推理成本,——从「搬东西 vs 算东西」说起
一篇写给非技术读者的科普文。读完你会理解:为什么 ChatGPT 是一个字一个字蹦的、为什么它能同时服务千万人、为什么超长上下文会突然涨价、以及为什么花再多钱也无法让它「瞬间回答」。
一、先建立一幅画面
当你在 ChatGPT 里按下回车,机器内部到底发生了什么?
要回答这个问题,我们需要先认识三个角色:参数、显存 和 计算单元。
1. 参数:模型其实就是一堆数字
你或许听过「GPT-5 有几千亿参数」「DeepSeek 是 700B 模型」这种说法。参数听起来高深,物理上其实非常朴素——就是一堆浮点数,排列成很多个矩阵。
一个 700B(7000 亿参数)的模型,按 FP8 精度(每个参数 1 字节)计算,体积大约是 700 GB。
你可以把它想成一本超厚的菜谱,写着 7000 亿条规则。但这本菜谱不能随手翻——它必须完整地放在一个能让厨师快速翻阅的地方,厨师才能开工。
2. 显存(HBM):能让厨师快速翻阅的「工作台」
这个「能快速翻阅的地方」,就是 GPU 的显存(HBM)。
🔑 关键事实 1:模型运行时,所有参数必须装在显存里。
一张 H100 GPU 只有 80 GB 显存,装不下 700 GB 的模型。所以现实中需要很多张 GPU 拼在一起——这就是「机柜」「集群」这些名词的由来。
3. 计算单元:真正干活的工人
GPU 内部还有一群计算单元,负责做乘法加法。可以把整个 GPU 想象成一座工厂:
- 计算单元 = 工人:动作飞快
- 显存 = 仓库:存着 700 GB 的参数
- 传送带(内存带宽):把参数从仓库搬到工人面前
现代 GPU 的尴尬就在这里——工人快得离谱,但仓库到工人的传送带不够快。
二、生成一个字,到底要做什么?
假设你问模型:「今天天气怎么样?」它要回答「今天阳光明媚」。
模型不是一次性把这五个字想出来的,而是一个字一个字蹦:
第 1 步:看到「今天天气怎么样?」 → 输出「今」
第 2 步:看到「今天天气怎么样?今」 → 输出「天」
第 3 步:看到「今天天气怎么样?今天」 → 输出「阳」
...这种「依赖前一个字、再生成下一个字」的方式,叫做自回归生成(autoregressive)。
而每生成一个字,模型的全部参数都要被完整地读一遍。
🔑 关键事实 2:每输出一个 token,所有参数都要被「搬运」一次。
输出 100 个字,就要把 700 GB 的参数搬 100 遍。这件事的成本,主导了一切。
三、慢,慢在哪?——一个反直觉的事实
你可能以为模型慢是因为算得慢。
不是。 真正慢的是把参数从显存搬到计算单元这件事。
一组数字感受一下:H100 的内存带宽约 3 TB/s,听起来很大,但要把 700 GB 的参数搬一遍,至少需要:
也就是 230 毫秒——这就是「在一张 H100 上跑 700B 模型」时,每生成一个字的物理下限。换算下来约 4 token/秒。
🔑 关键事实 3:大模型推理通常不是被「算力」卡住,而是被内存带宽卡住。
这种状态有个专门的名字:memory-bound(被内存带宽卡住)。与之对应的是 compute-bound(被计算卡住)。整个推理优化领域的故事,就是在这两者之间来回拉扯。
四、batch:让多个用户共享一次「搬运」
既然每输出一个 token 都要把参数搬一遍,那最浪费的事情是什么?
Alice 和 Bob 同时在用 ChatGPT。如果机器先给 Alice 算一个字(搬一遍参数),再给 Bob 算一个字(再搬一遍参数),那 700 GB 的参数就被搬了两遍。
但如果让 Alice 和 Bob 共用一次搬运——参数搬上来一次,顺便把两人的下一个字一起算了——就只搬一遍。
这就是 batch(批处理) 的核心动机:
🔑 batch 的本质:让一次「搬参数」被尽可能多的用户共享。
这也直接解释了一个你可能困惑过的现象:为什么 ChatGPT 能同时服务那么多人,而每个人感觉速度还差不多? 因为大家都在共享同一次搬参数。
注意:batch 只能跨「不同用户的同一步生成」分摊,不能跨「同一个用户的不同 token」分摊。因为同一个用户的 token 必须串行:第 N 个字依赖第 N-1 个字。
batch 的天花板
batch 不能无限大。回到工厂比喻:
- batch 很小时:工人很快算完,等传送带送下一批材料 → 瓶颈是传送带(memory-bound)
- batch 很大时:材料堆成山,工人根本算不过来 → 瓶颈是工人(compute-bound)
中间有个甜蜜点:工人刚好在下一批材料到达前算完。在这个点附近,系统效率最高。对现代 GPU 来说,这个值大致在 300 × 稀疏度 左右。
🔑 优化推理的核心矛盾:让传送带和工人尽量平衡,谁也别等谁。
五、KV cache:每个用户的「私人笔记」
到这里有个隐藏的麻烦还没解决。
模型在算 Alice 的第 51 个字时,需要「看到」前面的 50 个字。Bob 才刚开始,看到的是空的。模型怎么「看到」Alice 前面的 50 个字?
最笨的办法:把那 50 个字再输入一遍。但如果 Alice 已经聊了 1 万个字,每生成一个新字都要把前 1 万个字重新过一遍模型,那就是指数级浪费。
工程上的解法叫 KV cache:
🔑 KV cache 是模型对「已读过 token」的一份内部笔记。
打个比方:你在读一本超长的小说,边读边做笔记。读到第 1000 页时——
- 没 cache:每翻一页都从头再读前 999 页 → 疯了
- 有 cache:你早就把前 999 页提炼成了笔记,只需扫一眼笔记 → 正常人
每生成一个新 token,模型只需要:读自己历史的笔记 → 算出新 token → 把新 token 也记一条进去。
KV cache 的关键性质:私有,无法共享
这是整个故事里最重要的一点:
🔑 每个用户的 KV cache 是私有的,不能跨 batch 共享。
Alice 的笔记是关于 Alice 对话的,跟 Bob 一点关系都没有。所以读 Alice 的笔记这件事,只服务 Alice 一个人,加再多别的用户也分摊不掉。
这意味着:
| 项目 | 跟 batch 的关系 | 跟上下文长度的关系 | 能否被 batch 摊销 |
|---|---|---|---|
| 搬模型参数 | 固定 | 不变 | 能 ✅ |
| 搬 KV cache | 线性增长 | 线性增长 | 不能 ❌ |
| 计算 | 线性增长 | 几乎不变 | (这就是工作本身) |
理解这张表,就理解了大模型推理 80% 的内容。
六、用这套直觉解释三个现实现象
现象 1:超长上下文为什么会涨价?
Google Gemini 在 200K context 之后 API 涨价 50%。为什么?
- 上下文短时(比如几千 token):KV 笔记很小,主要成本是搬参数,能被 batch 充分摊销 → 便宜
- 上下文超过某个临界点(≈ 200K):KV 搬运的耗时开始超过参数搬运耗时 → 系统从 weight-bound 切换到 KV-bound,而 KV 成本摊不掉 → 必须涨价
200K 不是拍脑袋定的,而是算出来的物理临界点。
现象 2:output token 为什么比 input token 贵几倍?
- 处理 input:可以一次性把整段输入并行算完,工人吃满,compute-bound,便宜
- 生成 output:必须一个一个 token 蹦,每蹦一个都要搬一遍参数 + 自己的 KV,memory-bound,贵
现象 3:为什么花再多钱也无法「瞬间回答」?
哪怕你包下整台 GPU,只服务自己一个人,模型参数还是必须从显存搬到计算单元里。这件事的速度被物理定律(HBM 带宽)锁死,没有办法用钱绕过。
所以「花 100 倍钱换 100 倍速度」是不可能的。像 Claude 的 FastMode 之类的「加速档」,本质是把同样的 batch 分散到更多 GPU 上,等效带宽更大 → 单用户更快;但加到一定程度,卡之间通信本身也要时间,物理墙仍在那里。
七、一图总结
推理 = 「搬东西」+ 「算东西」
搬什么:
- 模型参数(大、固定,可被 batch 摊销)
- KV cache(每用户私有,跟用户数和上下文长度成正比,摊不掉)
一个 token 的耗时 ≈ max(搬运时间, 计算时间)
优化的核心:让传送带和工人尽量平衡,谁也别等谁。
八、留几个可以自己想的问题
- 如果一个公司声称自己的「推理服务比别人快 10 倍」,可能用了哪些手段?哪些是真的、哪些只是把 batch 换成了独享?
- 为什么 MoE(稀疏专家)模型在推理时反而会让最优 batch 变小?提示:去想「稀疏度」这个词。
- 如果未来 HBM 的带宽翻 10 倍,对大模型 API 价格会有什么影响?哪一项成本会主导被改写?