深入讲解 LLM 推理的完整流程,重点阐述 prefill 和 decode 两个核心阶段的工作原理及其性能影响。
你好,我是 Shrijith Venkatramana。我正在开发 git-lrc,这是一款会在每次 commit 时运行的 AI 代码审查工具。欢迎为我们点 Star,帮助更多开发者发现这个项目。也请试用并分享反馈,帮助我们改进产品。
有一群 LLM inference 爱好者,经常讨论 LLM serving。
人们会提到 prefill、decode、KV cache、continuous batching 和 paged attention 等术语。读了几篇文章后,我已经能认出这些词,却仍然回答不了一个最基本的问题:
向 LLM 发送 prompt 后,究竟发生了什么?
作为软件工程师,我们通常会按照事件发生的顺序来理解系统:Web 请求抵达服务器,middleware 开始运行,业务逻辑执行,最后返回响应。
我也希望用同样的思维模型理解 LLM。
本文将介绍这段探索之旅的第二部分。
简化后的请求流程如下:
Client
↓
Load Balancer
↓
LLM Server
↓
Tokenizer
↓
Prefill
↓
Decode
↓
Streaming Response
其中有两个重要阶段:
一旦理解了这两个概念,后面的许多主题也就顺理成章了。
假设 prompt 是:
像给十岁孩子讲解一样解释量子计算。
我的第一个问题是:
模型会立即开始生成响应吗?
答案比我预想的更简单。
模型首先会处理完整的 prompt。
这个阶段称为 prefill。
一个实用的思维模型是:
kvCache, firstToken := Prefill(prompt)
Prefill 会完成两项任务:
构建初始 KV cache。
生成第一个输出 token。
此时,模型已经掌握了继续生成响应所需的全部信息。
Prefill 完成后,响应的其余部分来自一项不断重复的操作。
cache, token := Prefill(prompt)
for token != EOS {
emit(token)
cache, token = Decode(cache, token)
}
每次调用 Decode() 都会执行三个动作:
生成下一个 token。
返回新的 cache。
这个循环会持续执行,直到模型生成 end-of-sequence token。
正是在这里,我第一次感觉 LLM inference 不再是一个黑盒,而是变得像普通软件一样容易理解。
这个名字一度让我以为,它是类似 Redis 或 Go map 的 cache。
实际上,它的概念要简单得多。
KV cache 是存储在 GPU 内存中的数据。
在 prefill 阶段,模型会为 prompt 中的每个 token 计算内部数据,并将其保存下来。
在 decode 阶段,模型会为每个新生成的 token 添加一条新记录。
可以用下面这个简单的思维模型来理解:
type KVCache struct {
Layer0 [...]
Layer1 [...]
Layer2 [...]
...
}
在整个对话过程中,cache 会持续增长。
它的用途很直接:
保存计算成本高昂的结果,让后续 token 能够复用。
假设一个 prompt 包含 2,000 个 token。
再假设模型生成了 500 个输出 token。
一种做法可能是:
读取这 2,000 个 prompt token。
再次读取同样的 2,000 个 prompt token。
生成下一个 token。
这会重复执行大量计算。
KV cache 改变了这一过程。
模型只会在 prefill 阶段读取一次 prompt。
到了 decode 阶段,它会复用 cache 中的信息,并为每个新生成的 token 添加相应数据。
计算成本高昂的工作只需执行一次。
这是我接下来想到的问题。
如果 prompt 中的每个 token 都会向 cache 添加数据……
那么,包含一百万个 token 的 prompt 会怎样?
根据前面的思维模型,答案显而易见。
每增加一个 token,cache 都会随之增长。
粗略估算一下,对于一个 7B 模型,每个 token 的内存占用大约会达到数百 KB。
于是就会得到这样的数字:
现代模型会使用 Grouped Query Attention(GQA)等技术减少这部分内存占用。多个 query head 共享相同的 Keys 和 Values,因此 cache 可以变得小得多。
关键点其实很简单。
长 context window 需要消耗大量内存,因为 KV cache 会不断增长。
这也解释了为什么现代 serving 系统要投入如此多的精力管理 KV cache。
理清这些概念后,我最终形成了一幅一直记在脑海里的图景。
Prompt
↓
Prefill(prompt)
↓
KV Cache + First Token
↓
Decode(...)
↓
Next Token
↓
Decode(...)
↓
Next Token
↓
...
这个模型可以回答许多基本问题。
为什么模型必须先读取 prompt,然后才能生成文本?
为什么 GPU 内存占用会随着 context length 增加?
为什么每个 serving framework 都在讨论 KV cache?
为什么 serving 优化都集中在 prefill 和 decode 上?
关于 attention、batching、scheduling 和 parallelism,我仍然有很多疑问。
但现在,当我学习这些概念时,已经有了一个可以把它们串联起来的框架。
LLM inference 中,哪个部分让你花了最长时间才真正理解?是 attention、KV cache、batching,还是其他概念?
*AI Agent 编写代码的速度很快。但它们也会悄无声息地删除逻辑、改变行为并引入 bug,而且不会告诉你。你往往要等到生产环境出问题时才会发现。
git-lrc 可以解决这个问题。它会接入 git commit,在每个 diff 落地之前进行审查。只需 60 秒即可完成设置,而且完全免费。*
欢迎提供任何反馈,也欢迎贡献者加入!它已经上线、source-available,任何人都可以直接使用。
在 Git Commit 时运行的免费微型 AI 代码审查
| 🇩🇰 丹麦语 | 🇪🇸 西班牙语 | 🇮🇷 波斯语 | 🇫🇮 芬兰语 | 🇯🇵 日语 | 🇳🇴 挪威语 | 🇵🇹 葡萄牙语 | 🇷🇺 俄语 | 🇦🇱 阿尔巴尼亚语 | 🇨🇳 中文 | 🇮🇳 印地语 |
在 Commit 时运行的免费微型 AI 代码审查

如今的 GenAI 就像一辆没有刹车的赛车。它加速迅猛——你只需描述需求,大段代码便会瞬间出现。但 AI Agent 也会悄无声息地破坏系统:删除逻辑、放宽约束、引入成本高昂的云服务调用、泄露凭据,甚至改变行为,而且不会告诉你。你往往要等到生产环境出问题时才会发现。
git-lrc 就是你的制动系统。它会接入 git commit,在每个 diff 落地之前运行 AI 审查。只需 60 秒即可完成设置,而且完全免费。
简而言之,git-lrc 可以帮助你在事故发生之前,预防服务中断、安全漏洞和技术债。
快速概览:10 类风险 · 跟踪 100 多种故障模式 · 覆盖每一次 commit……
如需采取进一步措施,你可以考虑屏蔽此人和/或举报滥用行为。