GPU 与模型部署 · B 推理与部署
0 / 7 章 ← 首页
贯穿案例 · 一个 14B 模型的一生 序幕 · 在个人电脑跑起来 第二幕 · 变成代码助手 第三幕 · 微调成知识助手 第四幕 · 上线 50 人服务
01

一次推理是两幕戏 —— 先读题,再逐字写

同一个问题,提示词贴了 3 万字就等得心焦,可一旦第一个字蹦出来,后面就哗哗地流。这不是错觉,也不玄学:一次推理其实是两幕戏,两幕的瓶颈根本不是同一种资源。看懂这一章,后面量化的「为什么压完反而快」才有因果落点。

用后厨打比方:用户的问题是一张订单。Prefill 是读题审菜——把订单上所有要求一次性看完、备好料;Decode 是颠勺出菜——一道菜一道菜地往外端,端一道才能准备下一道。

第一幕 · Prefill(读题) 整个 prompt 的 N 个 token 一次并行算完 产出 KV Cache(草稿纸) 瓶颈:算力 compute-bound → 决定 TTFT 第二幕 · Decode(逐字写) 自回归:一步只生成 1 个 token 每步都要回读全部权重 + 已有 KV 瓶颈:显存带宽 memory-bound → 决定 tokens/s tok₁ tok₂ tok₃ …N 个一起算 并行度高:几百个 token 填满几千个 CUDA 核心,Tensor Core 吃满 w₁ w₂ w₃ 每写一个字都要把整套权重从显存搬一遍 → 大部分时间在等数据 粗算:prefill 速度 ≈ TFLOPS × MFU ÷ (2×参数量/词);decode 速度 ≈ 显存带宽 ÷ 权重大小(见下方动画实时计算)
PREFILL vs DECODE · 两幕的并行度差几百倍,瓶颈因此不同

两个时间指标,各归各的幕

TTFT(Time To First Token,首 token 延迟)由第一幕 Prefill 决定——题越长,读题越久,等你感觉就是「按了回车半天没动静」。每 token 间隔(其倒数即 decode 速度 tokens/s)由第二幕决定——只跟「权重要读多少字节」和「显存带宽多粗」有关,跟你 prompt 有多长基本无关。下面的直觉表由本页脚本按共享公式(14B 量化到 4-bit、RTX 4090)实时算出:

提示词长度TTFT(理论量级)之后的 decode 速度体感
一句话记住:首字慢,慢在算力(读题);后字稳,稳在带宽(写字)。优化 TTFT 要动算力侧(更快的卡、更短的 prompt、前缀缓存),优化流式速度要动字节侧(量化、更宽带的卡)。
B1 · 推理时间线动画14B · 4-bit · RTX 4090(数字由 GPU_CALC 实时计算)
待播放 —— 请求到达
PREFILL
请求到达t = 0.00 s完成
拖动参数或直接播放
TTFT(prefill)—
每 token 间隔—
decode 速度—
总耗时—
啊哈点:把提示词从 512 拉到 16384 —— 蓝色 prefill 块吃掉整条时间线,TTFT 飙升;再把生成长度拉满 —— 解码区只是格子变多,每个格子的节奏(每 token 间隔)纹丝不动。

深挖 · 为什么两幕的瓶颈不同?

并行度是根。Prefill 一次有几百上千个 token 同时进矩阵乘法,几千个 GPU 核心都有活干,此时算力(FLOPS)是稀缺品;Decode 一步只有 1 个 token,算它只需要极小的算力,但注意力要回看全部历史 KV、前馈要读全部权重——时间都花在「把数据从显存搬过来」的路上。这就是 roofline 模型里 compute-bound 与 memory-bound 的分界,也是 FlashAttention、Tensor Core 这些优化主要惠及 prefill、而 decode 更吃带宽的原因。出处:vLLM 论文 §2(arXiv:2309.06180)对此有标准论述。
章内小自测:为什么把 prompt 缩短能明显加快「第一个字」,却几乎不影响「后面每个字」的节奏?
缩短 prompt 减少的是 Prefill 要并行处理的 token 数 → TTFT 直接变短。而 Decode 每步的时间由「读全部权重 + 已有 KV」决定,少一点 prompt 只让 KV 略小,每 token 间隔几乎不变。所以「答案出来后流得快不快」基本与 prompt 长度无关。
✦ 本章通关条件
✓
能说清 TTFT 由 Prefill(算力瓶颈)决定、流式速度由带宽÷权重量决定
02

量化 —— 把精装菜谱压成口袋本

14B 模型 BF16 要 ≈29.6GB 显存(按 config 的 14.8B 参数 × 2 字节,共享公式库口径),一张 4090 装不下;压到 4-bit 只剩约四分之一,还顺手变快了。天底下没有免费的午餐——效果掉了多少?掉的速度值不值?这一章给你一套能拍板的判断框架。

精度谱系:质量、显存、速度的取舍三角

把参数从「精装版」抄成「缩印口袋本」,字变小、本子变薄,但极端情况下会抄错字。每参数字节数来自共享公式库 GPU_CALC.PRECISIONS:

精度字节/参数14B 权重一句话画像
FP324 B—训练主权重口径;推理几乎不用,纯浪费
BF16 / FP162 B—开源模型发布的主流精度;质量基准线
FP8 / INT81 B—数据中心推理新贵(H 系卡原生支持),几乎无损
INT4 / NF40.5 B—本地部署与 QLoRA 的主力档;质量损失通常很小(经验值)

4-bit 也分门派——都是「训练后量化(PTQ)」,压法不同:

  • GGUF(K-quants,如 Q4_K_M):llama.cpp 生态通用格式,按块混合位宽,Q4_K_M 实际平均约 4.8 bit/参数,是本地部署的事实标准。
  • GPTQ:逐层最小化重构误差的校准式量化,GPU 服务端老牌选择(Frantar et al., 2022)。
  • AWQ:激活感知——只精确保护对输出影响大的少数关键权重,其余狠压(Lin et al., 2023),vLLM 服务端常见。
  • bitsandbytes NF4:QLoRA 的存放格式(信息论上 4-bit 的优分布),c 页微调时再细讲。
经验结论(标注为经验值,非严格基准)① 14B 这个量级的模型,4-bit 量化在常见任务上的质量损失通常很小,多数场景值得;② 模型越小,量化伤害越大——0.5B/1.5B 压到 4-bit 容易明显变笨,小模型建议 Q5/Q6 起步;③ 权重变小 → decode 每 token 读的字节变少 → 流式输出变快,这正是章 1「带宽÷权重量」公式的直接推论。
B2 · 量化四方权衡器显存 / 速度由 GPU_CALC 计算 · 质量条为示意非基准
14B 权重显存——
理论 decode 上限(4090)—带宽 ÷ 权重量,实际打 5~7 折
回答质量(示意)
—
适用建议—
⚠ 能跑但答非所问Q2 已越过实用底线:权重平均只剩 2 bit,模型会流畅地胡说。仅用于极限显存下的玩具验证,不要用于任何要交付答案的场景。

对照查证:GGUF Q4_K_M 的实际文件 ≈ 9.0 GB(Ollama `qwen2.5:14b` 默认档,查证 2026-09),比纯 4-bit 理论值 7.4 GB 大两成——K-quants 部分层用更高位宽,文件里还有 embedding、词表与元数据。
✦ 本章通关条件
✓
能给一个 14B 模型选精度档位,并说出显存 / 速度 / 质量三方面的代价
03

跑起来 —— Ollama / llama.cpp / vLLM / SGLang 怎么选

教程一会儿让你 ollama run,一会儿让你装 vLLM,它们是同类东西吗?不是——它们处在不同楼层:底下是引擎,中间是开箱即用的封装,上面是生产级服务框架。选错楼层不会报错,但会让你在错误的战场上挣扎。

工具定位分层(口径核对 2026-09)

层级工具定位一句话
引擎层llama.cppC/C++ 推理引擎,GGUF 格式发源地,CPU/GPU 混合,Ollama 与 LM Studio 的底层后端(查证 2026-09,官方仓库口径)
开箱即用层Ollama / LM StudioOllama = 一条命令的 CLI;LM Studio = 给非命令行同事的 GUI。个人本地首选,但服务并发能力弱
生产服务层vLLM在线服务引擎主力:continuous batching + PagedAttention(章 4 主角),单卡/多卡生产部署的事实标准
生产服务层SGLang与 vLLM 同层竞争:RadixAttention 前缀缓存自动复用(基准命中 50~99%),结构化输出性能突出(查证 2026-09,SGLang 论文 arXiv:2312.07104 及官方仓库)
极限优化层TensorRT-LLMNVIDIA 深度优化,追极限吞吐再用;代价是构建链路重、绑 NVIDIA 生态(查证 2026-09,官方 README 口径)

硬件路线同样分层:Mac(统一内存当显存使,14B 4-bit 装得下,带宽 546GB/s 量级 → 理论 decode ≈74 tok/s、实际打 5~7 折,安静省电,但服务生态弱);NVIDIA 消费卡(生态全、快,24GB 是 14B 的甜点);云 GPU(免持有成本、按时计费,个人长年开机反而贵)。带宽/显存数字均取自共享公式库 GPU_CALC.GPUS。

B3 · 部署工具决策树选任务 × 选硬件 → 高亮路径 + 起步命令
① 任务
→
② 硬件
→
③ 推荐工具
一句话理由—
起步命令
命令以各官方文档为准(2026-09 口径核对);换了模型/卡型后参数需相应调整。

贯穿案例第二幕落地:把 14B 变成你的代码助手

现在主角登场。在自己的机器上把它跑起来,全程三条命令:

terminal · 14B 第二幕
# 1. 拉模型并对话(默认即 Q4_K_M 量化,下载约 9.0 GB,查证 2026-09)
ollama run qwen2.5:14b        # 想要代码特化:ollama run qwen2.5-coder:14b

# 2. 看它此刻的真实占用(权重 + KV + 运行时开销,一并列在 SIZE)
ollama ps                     # SIZE 约 10~12 GB、PROCESSOR 显示 100% GPU 即全程在卡上

# 3. 打开本地 API(默认 11434 端口,OpenAI 兼容接口)
ollama serve
对照 A 页估算器理论账:权重(4-bit)7.4 GB + 单人 4K 上下文 KV ≈0.8 GB + 运行时开销 ≈1.7 GB ≈ 9.9 GB(`GPU_CALC.inferenceGB` 口径)——和 ollama ps 看到的量级一致。自己用(并发 1)24GB 卡绰绰有余。至于快不快:理论 decode ≈136 tok/s(1008GB/s ÷ 7.4GB),打 5~7 折即 70~95;社区实测 4090 跑 14B Q4 约 80~110 tok/s(查证 2026-09,llama.cpp 社区基准)。回 A 页显存估算器亲手验一遍 →
✦ 本章通关条件
✓
能按「任务 × 硬件」选出对的工具楼层,并说出 llama.cpp / Ollama / vLLM / SGLang / TensorRT-LLM 各自的定位
04

单人能用,为什么 8 个人就崩 —— 并发、批处理与显存排队

自己用行云流水,拉个群里 8 个人同时提问,全都转圈,nvidia-smi 一看 GPU 利用率还不高——机器没满载却集体卡死,这是新手服务化的第一课。三个原因叠加,一个比一个隐蔽。

原因一:并发 × 上下文,KV Cache 的乘法爆雷

A 页讲过:每 token 的 KV ≈ 0.19 MB(14B 的 GQA 配置,BF16)。单人 4K 上下文才 0.8 GB,无感;但 KV 是乘法——总 KV = 并发数 × 上下文 × 每 token 开销。下表由本页脚本调 GPU_CALC.kvTotalGB 实时计算,目标是「24GB 卡、14B 4-bit」的 KV 预算 ≈13.7 GB(可用显存 22.8 − 权重 7.4 − 开销 1.7):

场景总 KV占 KV 预算判定

「代码助手」恰恰是长上下文场景(贴一整个文件、repo map),8 个人 × 32K 就要 ≈51 GB KV——三张 4090 都填不满。崩的种子在 A 页就埋下了,这里只是爆雷。

原因二:逐个处理,GPU 大部分时间在等

朴素做法是排队:一个请求做完再做下一个。可 decode 阶段每步只算 1 个 token 却要读一遍全部权重——带宽被一个请求独占,算力闲得发慌。与其一人一份单独炒,不如大锅一起炒:把多个请求的 decode 步拼成一个批次,读一遍权重同时给 8 个人各算一个 token,几乎不花额外时间。

静态批:凑一批 → 等最慢的做完 → 换班 请求A(短) 请求B(长)—— A 早就完了也在陪跑 A 空转等待 空窗:等新批凑齐空窗 连续批(Orca,OSDI'22):按「生成一步」为单位随时进出 A:完成后立即腾位 B:一直算到自然结束 C:中途到达,下一步就插进来 C 到达 A 完成 · 位子给 C
STATIC BATCH vs CONTINUOUS BATCHING · 迭代级调度,短请求不再陪跑(Orca 论文思想)

原因三:显存分配的长尾与 OOM

就算算力够,KV 显存的分配方式还能再坑你一次:传统做法给每个请求预留「最长序列」的连续显存,实际平均只用三四成,剩下的全部碎片化浪费——「明明还剩 3GB 却分配不出 2GB」。vLLM 的 PagedAttention 像操作系统管理内存分页一样把 KV 切成小块按需分配,把浪费压到近零(原论文口径:KV 显存浪费 <4%,对比原系统的实际利用率仅 20~38%;查证 2026-09,arXiv:2309.06180)。

服务端三个旋钮(vLLM 默认值查证 2026-09,官方 Engine Args 文档)① max_num_seqs:同时处理的序列上限,默认 256——显存紧就调小;② gpu_memory_utilization:允许引擎吃掉的显存比例,默认 0.9;③ 队列与超时:排队的上限和等待超时,配合章 5 的限流一起用。
B4 · 压轴 · 并发模拟器:逐个处理 vs 连续批处理离散事件模拟 · 数字来自 GPU_CALC
逐个处理(串行)连续批处理
聚合吞吐(tok/s)
平均 TTFT(s)
显存水位(% of 24GB)
逐个处理连续批处理
啊哈时刻:并发从 1 拉到 4、再到 16 —— 连续批的吞吐近乎线性涨、平均 TTFT 只略增;再往上撞到的墙是显存(KV 装不下 → 拒新请求),而不是算力。吞吐的墙是显存,不是算力。

深挖 · 模拟器做了哪些简化?

为了在浏览器里跑出干净的对比,本模拟做了取舍:① prefill 与 decode 各走各的流水线,未模拟 chunked prefill 与两阶段争抢算力;② 批内每请求吞吐取 min(单流速度, 聚合算力上限/批大小),未细化注意力随上下文的二次项;③ OOM 建模为「KV 达到预算即拒绝新请求」,真实 vLLM 会先排队、再抢占(preempt)重算,最终表现相近:都是显存墙在限流。定性结论(吞吐墙在显存准入、连续批碾压串行)与论文一致;要精确数字请以 vLLM/SGLang 实测为准。
章内小自测:同样是 24GB 卡跑 14B 4-bit,为什么「8 人 × 4K」能活、「8 人 × 32K」必死?
权重与开销固定 ≈9.1GB,剩下 KV 预算 ≈13.7GB。每路 4K 上下文的 KV ≈0.8GB,8 路共 ≈6.4GB,装得下;每路 32K 则是 8 倍 ≈6GB/路、8 路共 ≈51GB,连一张卡都装不下——KV 随上下文线性涨、随并发乘性涨(GPU_CALC.kvTotalGB)。
✦ 本章通关条件
✓
能解释并发 × 上下文如何打爆显存,以及连续批处理 + PagedAttention 分别救了什么
05

限流与降级 —— 崩之前的工程动作

章 4 的结论有点反直觉:高明的服务不是不拒绝,而是拒绝得漂亮。与其让所有人一起卡死、让显存爆掉把整组进程带走,不如主动拦住一部分请求——被拒绝的用户重试就好,崩掉的进程会连没被请求的无辜者也一起拖下水。

限流三件套

  • 并发上限:同时处理的请求数封顶(服务端 max_num_seqs + 应用层信号量双重保险),超出直接快速失败。
  • 排队 + 超时:峰量先排队缓冲,但队列要有长度上限和等待超时——「排 30 秒还没到就明确告诉他再试」,好过让人盯着转圈两分钟。
  • 按用户配额:防止单个用户(或一个失控脚本)吃光全部容量;按 API key / 租户分桶计数。

降级路径:一条预演好的撤退路线

降级不是临时拍脑袋,而是提前写进代码的分支。按代价从小到大依次退:

触发动作用户感知
请求带超长上下文引导缩短/摘要后再来,或服务端截断几乎无感
高峰(水位 > 阈值)临时切更小的量化档 / 更小的模型回答略变笨,但还在流式出字
本地集群濒临崩兜底转发商业 API无感(成本变高,钱包有感)

省算力的正道:前缀缓存复用

限流是「少花钱」,缓存是「少干活」。如果所有请求共享同一段很长的 system prompt(代码助手几乎都是这样),那这段前缀的 prefill 结果——KV Cache——本可以跨请求复用,不必每个请求都重读一遍题。vLLM 的 automatic prefix caching、SGLang 的 RadixAttention(用基数树管理前缀复用,基准命中率 50~99%,查证 2026-09)都是服务端实现;托管 API 的 prompt caching 则把同一机制反映到账单上(命中部分按一折左右计费)。

交叉阅读本仓库另一部手册 《LLM Prompt Caching · 前缀缓存实验室》 从 API 计费与规则侧讲同一件事(怎么组织 prompt 才能命中、各厂商写/读价与断点规则);本页关心的是服务端 KV 复用机制。两边对照读,「省 TTFT」和「省钱」是一体两面。

监控最小集(d 页细化)

限流和降级都要靠指标触发。最小四件套先记住名字:TTFT p95(体验崩没崩)、tokens/s(吞吐够不够)、显存水位(离墙多远)、错误率(是不是已经在拒绝)。阈值怎么定、告警发给谁、崩了怎么演练,留到 D 页 · 选型与运维 收官。

✦ 本章通关条件
✓
能说出限流三件套与「缩上下文 → 降档 → 商业 API」的降级顺序
06

终极测验:推理与部署,你过关了吗?

5 题验收。答对 4 题以上,你就有资格去谈「给我们团队部署一个模型」了。

FINAL EXAM · INFERENCE & DEPLOYMENT0 / 5
✦ 本章通关条件
✓
终极测验 ≥ 4 题(答到 4 题会自动打勾)
07

参考资料 · References

本页所有关键数字的出处。带「查证 2026-09」的条目为写作时逐一核对过的事实;硬件量级来自共享公式库 calc.js(a/b/c/d 四页唯一数据源)。

← 上一页 · A 底层认知 下一页 · C 微调实战 →