1. 从"跑得动"到"跑得快":推理优化到底在解决什么问题
很多人第一次把大模型跑起来的时候,注意力全在"能不能出结果"上——模型权重下载完、环境配好、一行命令敲下去,屏幕上开始一个字一个字往外蹦,那一刻的成就感确实很足。但真正把它放到业务场景里,问题立刻就变了:响应太慢、显存吃紧、并发一上来就崩、单次调用成本高得离谱。这时候你才会意识到,推理优化不是锦上添花的技巧,而是决定这套系统能不能真正用起来的分水岭。
我见过太多团队卡在这个阶段。模型明明已经部署成功了,Demo 演示也没问题,可一旦要支撑真实流量,就发现单张卡只能扛个位数并发,延迟动辄十几秒,用户等不了,老板也等不了。于是开始到处找"加速方案",看到别人说量化好就上量化,看到别人说 vLLM 快就换 vLLM,结果改了一圈,效果时好时坏,根本说不清到底哪一步起了作用。
这个训练营想解决的就是这个问题。它面向的不是"从没接触过大模型"的纯小白,而是那些已经把模型跑起来、但被性能问题卡住的开发者、算法工程师和技术负责人。核心目标很明确:让你搞清楚推理链路上每一个环节在消耗什么、瓶颈在哪里、有哪些成熟的优化手段、每种手段的适用边界和代价是什么。学完之后你不只是会抄几个配置,而是能针对自己的硬件和业务场景,独立设计出一套合理的优化方案。
在展开具体内容之前,先把"推理优化"这个词拆开看。它其实包含三个互相拉扯的目标:延迟(Latency)、吞吐(Throughput)、成本(Cost)。延迟是单个请求从发出到收到完整回复的时间,用户最直观的感受;吞吐是单位时间内系统能处理的请求数量,直接决定你的服务能力;成本则是每处理一个 token 或每个请求所消耗的算力资源折算成的钱。这三者往往互相矛盾——想降低延迟,可能会牺牲吞吐;想提高吞吐,单请求延迟可能上升;想压成本,又可能两头都受影响。推理优化的本质,就是在这三者之间找到适合你业务的那个平衡点,而不是盲目追求某一个指标的最优。
举个具体的例子。如果你做的是实时对话产品,用户盯着屏幕等回复,那延迟就是第一优先级,哪怕吞吐低一点、成本高一点也得忍;如果你做的是离线批量处理,比如给几百万条历史数据打标签,那吞吐和成本才是关键,单条延迟高到几十秒都无所谓。这两种场景下,最优的优化策略可能完全相反。所以训练营里反复强调的一件事就是:先明确你的场景约束,再谈优化手段,脱离场景谈"哪个方案最好"是没有意义的。
2. 推理性能的账本:算力、显存、带宽到底谁在拖后腿
要优化,先得会算账。大模型推理的性能瓶颈,绝大多数情况下逃不出三个地方:计算、显存容量、显存带宽。搞清楚你的瓶颈在哪一个,优化才有方向,否则就是瞎调。
2.1 用"两阶段"视角理解推理的计算特征
大模型的推理过程分成两个阶段,这两个阶段的性能特征截然不同,理解它们是所有优化的基础。
第一个阶段叫Prefill(预填充),也就是模型处理你输入的整段 prompt 的过程。这个阶段所有输入 token 是并行计算的,矩阵乘法的规模很大,GPU 的计算单元能被充分喂饱,所以它是计算密集型的。你输入的 prompt 越长,这个阶段耗时越久,而且耗时大致和 prompt 长度成正比。
第二个阶段叫Decode(解码),也就是模型一个 token 一个 token 往外生成的过程。每生成一个 token,都要把前面所有 token 的 KV 缓存读一遍,然后做一次前向计算。问题在于,单次只生成一个 token,计算量很小,但需要读取的 KV 缓存却随着序列变长而线性增长。这时候 GPU 的算力根本用不满,大部分时间都花在从显存里搬数据上,所以它是显存带宽密集型的。
这个区别极其关键。很多人优化了半天没效果,就是因为搞错了瓶颈:明明卡在 Decode 阶段的显存带宽上,却去优化计算效率,自然没用。反过来,如果你的场景是长 prompt、短输出(比如文档问答),那 Prefill 阶段的计算才是大头,优化方向又不一样。
2.2 显存容量的硬约束:模型权重、KV 缓存、激活值
显存是很多人第一个撞上的墙。一张卡就那么多显存,模型权重、KV 缓存、中间激活值都要抢这块地。
模型权重是最刚性的部分。一个 7B 参数的模型,如果用 FP16 存储,光权重就要占大约 14GB;70B 的模型 FP16 下要 140GB 左右,单张消费级卡根本放不下。这就是为什么量化如此重要——把权重从 FP16 压到 INT8 或 INT4,显存占用直接砍半甚至砍到四分之一,很多原本跑不动的模型就能跑起来了。
KV 缓存是第二个大头,也是最容易被低估的。它的大小和批大小、序列长度、层数、注意力头数、头维度都成正比。序列越长、并发越高,KV 缓存膨胀得越厉害。实际部署中经常出现的情况是:模型权重加载完还剩不少显存,你以为能开大 batch,结果跑起来没几个请求就 OOM 了,罪魁祸首就是 KV 缓存。这也是后面要讲的 PagedAttention 这类技术要解决的核心问题。
中间激活值相对小一些,但在大 batch 或长序列下也不能忽略。它的大小和批大小、序列长度、隐藏层维度相关,通常在几十 MB 到几 GB 之间波动。
2.3 显存带宽:Decode 阶段真正的隐形杀手
前面说了 Decode 阶段是带宽密集型的,这里展开讲讲为什么。
假设你有一个 7B 的 FP16 模型,权重约 14GB。每生成一个 token,理论上都要把这 14GB 权重完整读一遍(简化理解,实际有缓存和复用)。如果一张卡的显存带宽是 1TB/s 左右,那读一遍权重就要 14ms 左右。也就是说,光是把权重读进来,每个 token 就要花十几毫秒,这还没算 KV 缓存和其他开销。生成 100 个 token,就是 1.4 秒起步。这就是为什么单请求的生成速度有个物理上限,再怎么优化计算也突破不了带宽这道墙。
理解了这一点,很多优化手段的原理就清楚了:量化之所以能加速 Decode,不只是因为省显存,更因为 INT8/INT4 的数据量更小,读取同样多的权重需要的带宽更少,所以每个 token 的生成时间能明显缩短。同理,批处理之所以能提升吞吐,是因为多个请求可以共享同一次权重读取,把带宽利用率拉满。
下面这张表把三个瓶颈的特征和典型应对手段梳理一下,方便对照自己的场景判断:
| 瓶颈类型 | 典型表现 | 主要出现在 | 常用应对手段 |
|---|---|---|---|
| 计算受限 | GPU 利用率高,算力打满 | Prefill 阶段、短序列 | 算子融合、更快的注意力实现、张量并行 |
| 显存容量受限 | 加载模型或开大 batch 时 OOM | 大模型、长序列、高并发 | 量化、KV 缓存优化、模型并行 |
| 显存带宽受限 | GPU 利用率低,生成速度上不去 | Decode 阶段 | 量化、批处理、投机解码 |
提示:判断瓶颈最直接的办法是用 profiling 工具看 GPU 利用率和显存带宽占用。如果算力利用率长期低于 30% 而带宽接近打满,基本可以确定是带宽瓶颈,优先考虑量化和批处理。
3. 量化:用精度换性能,但账要算清楚
量化是推理优化里性价比最高的手段之一,也是训练营里重点实操的内容。它的核心思想很简单:用更低的数值精度来存储和计算,减少显存占用和带宽消耗。但具体怎么做、做到什么程度、会损失多少精度,里面的门道不少。
3.1 FP16、INT8、INT4 的取舍逻辑
先理清几种常见精度的区别。FP16 是半精度浮点,也是目前大多数模型推理的默认精度,数值范围够用、精度损失可忽略。INT8 是 8 位整数,显存占用直接减半,带宽需求也减半,理论上 Decode 速度能提升接近一倍。INT4 更激进,显存再砍一半,但精度损失开始变得明显,需要更精细的处理。
量化的关键难点在于:浮点数转成整数会丢失信息,怎么把损失控制到最小。最朴素的做法是直接对权重做线性映射,但这样效果往往不好。实践中更常用的是分组量化——把权重按通道或按块分组,每组单独计算缩放因子和零点,这样能更好地适应权重的分布差异。分组越细,精度保留越好,但元数据开销也越大,需要权衡。
还有一个重要区分:权重量化和激活量化。只量化权重相对简单,因为权重是静态的,可以离线处理好;激活值是动态的,每个输入都不一样,量化起来难度大得多,对精度的影响也更敏感。所以很多方案只做权重量化(W8A16、W4A16),激活值仍用 FP16,这样能在保证精度的同时拿到大部分显存和带宽收益。
3.2 主流量化方案的实测对比
训练营里会带着大家实际跑几种主流方案,这里先给个预期。GPTQ 是训练后量化里比较成熟的一种,支持 4bit 和 8bit,对显存敏感的场景很友好,缺点是量化过程本身需要一些时间,而且对某些模型结构支持不够好。AWQ 是另一种训练后量化方法,它的思路是识别出对输出影响大的"重要权重"并加以保护,实测下来在 4bit 下精度保留通常比朴素 GPTQ 更好,是目前比较推荐的方案之一。GGUF 格式则更多用于 CPU 或混合推理场景,量化粒度选择丰富,从 2bit 到 8bit 都有,适合资源受限的环境。
需要提醒的是,量化不是无损的,也不是所有模型都适合激进量化。有些模型在 4bit 下表现依然稳健,有些模型掉点就很明显。所以量化之后一定要做评测,用你自己的业务数据跑一遍,对比量化前后的输出质量,而不是只看困惑度这种通用指标。我踩过的坑就是:某个模型 4bit 量化后通用评测看着还行,但一到具体的结构化抽取任务上就开始胡言乱语,最后只能退回 8bit。
3.3 量化实操中的精度验证方法
量化做完怎么验证?我的经验是分三层。
第一层是通用能力快速筛查,用一些标准评测集跑一遍,看有没有明显掉点,这一步主要是排除量化过程本身出错的情况。第二层是业务数据对比,拿一批真实输入,分别用原始模型和量化模型跑,人工或自动对比输出差异,重点看关键信息有没有丢失、格式有没有跑偏。第三层是边界情况测试,专门挑那些容易出问题的输入,比如超长文本、特殊符号、多轮对话,看量化模型会不会崩。
这三层做完,你才能对量化方案是否可用有个靠谱的判断。别嫌麻烦,量化上线后出问题再回滚,代价比这大得多。
4. 批处理与调度:把 GPU 喂饱的艺术
如果说量化是"省着用",那批处理就是"用满它"。GPU 最怕的就是闲着,而单请求推理恰恰让 GPU 大量时间处于等待状态。批处理的核心思路是把多个请求攒到一起,共享同一次计算和权重读取,从而把吞吐拉上去。
4.1 静态批处理为什么在真实场景里不好用
最朴素的批处理是静态批处理:攒够 N 个请求,一起送进模型,等全部生成完再一起返回。听起来简单,但真实场景里问题很大。
首先是请求长度参差不齐。有的请求输入几十个 token,有的几千个;有的输出一句话就结束,有的要生成上千 token。静态批处理要么按最长的来 padding,浪费大量计算;要么就得等所有请求都凑齐且长度接近,调度延迟高得离谱。其次是请求到达时间不确定,你没法保证同一批请求能同时到齐,等待凑批的时间可能比推理本身还长。最后是一个请求卡住,整批都受影响,用户体验很差。
所以静态批处理基本只适合离线批量任务,在线服务里很少直接用。
4.2 连续批处理:让每个请求独立进退
连续批处理(Continuous Batching)是现在在线推理的主流方案,vLLM、TensorRT-LLM 这些框架都支持。它的核心改进是:不再等整批请求全部完成,而是以 token 为粒度调度。某个请求生成完了就立刻退出批次,空出来的位置马上让新到达的请求补上。这样 GPU 始终有活干,利用率大幅提升,同时新请求也不用等前面所有请求都结束才能开始。
这个机制带来的吞吐提升非常可观,实测中相比静态批处理经常能有好几倍的差距。而且它对请求长度差异的容忍度很高,长短请求混在一起也不会互相拖累太多。
不过连续批处理也有代价:调度逻辑更复杂,对 KV 缓存的管理要求更高,因为批次里的请求随时在变,缓存要能动态分配和回收。这就引出了下一节要讲的 PagedAttention。
4.3 PagedAttention 与 KV 缓存的分页管理
KV 缓存的管理是连续批处理的关键难点。传统做法是给每个请求预分配一块连续的显存,按最大可能长度来分。问题是:大部分请求根本用不到那么长,预分配的空间大量浪费;而且显存碎片化严重,想开大 batch 也开不起来。有研究指出,传统方案下 KV 缓存的实际利用率可能只有两三成,剩下的全浪费了。
PagedAttention 借鉴了操作系统虚拟内存分页的思路,把 KV 缓存切成固定大小的块(block),按需分配,不要求连续。每个请求维护一张块表,记录自己的缓存分散在哪些块里。这样一来,显存利用率大幅提升,碎片问题也缓解了,能支撑的并发数明显增加。同时因为块是固定大小的,内存分配和回收都很快,非常适合连续批处理这种动态场景。
实测下来,开启 PagedAttention 后同样的显存能支撑的并发请求数经常能翻倍甚至更多,这对成本敏感的业务来说是实打实的收益。
4.4 调度策略对延迟和吞吐的实际影响
批处理不是越大越好。批次越大,吞吐越高,但单个请求的延迟也会上升,因为要等更久才能轮到、计算资源被更多请求分摊。所以调度策略要在延迟和吞吐之间做权衡。
常见的做法是设置一个最大批大小和最大等待时间:攒批时如果达到最大批大小就立即执行,或者等待时间超过阈值也执行,避免请求无限期等待。这两个参数需要根据你的业务延迟要求来调。实时对话场景下等待时间要设得很短,宁可批次小一点;离线场景下可以设长一点,把批次攒大。
还有一个技巧是优先级调度,给不同请求打不同优先级,重要的请求优先处理。这在混合负载场景下很有用,比如同时有实时请求和后台任务时,保证实时请求的体验。
5. 推理框架选型:别被"最快"的宣传带偏
市面上的推理框架不少,vLLM、TensorRT-LLM、SGLang、llama.cpp 各有侧重。选型的时候最忌讳的就是只看别人benchmark里的"最快",因为那些数据往往是在特定硬件、特定模型、特定负载下测出来的,换个场景可能完全不一样。
5.1 vLLM、TensorRT-LLM、SGLang 的定位差异
vLLM 是目前社区最活跃、上手最友好的方案之一,PagedAttention 和连续批处理都是它的招牌能力,对 HuggingFace 模型的支持很好,改造成本低。它的优势是通用性强、生态好、文档全,适合大多数团队作为默认选择。缺点是极致性能上可能不如专门优化的方案。
TensorRT-LLM 是偏底层的方案,需要把模型编译成 TensorRT 引擎,过程相对繁琐,对模型改动也更敏感。但换来的是极致的性能,尤其在特定硬件上,经过充分调优后延迟和吞吐都能做到很好。适合对性能有极致要求、且愿意投入工程成本的团队。
SGLang 在结构化生成和复杂调度上有独特优势,比如需要约束输出格式、做前缀共享的场景,它的 RadixAttention 能显著提升缓存复用率。如果你的业务涉及大量共享前缀的请求(比如固定的系统提示词),值得重点考虑。
llama.cpp 则主打轻量和 CPU/混合推理,量化支持丰富,适合资源受限或边缘部署的场景。它的性能当然比不上 GPU 方案,但在没有 GPU 或者想低成本跑起来的时候非常实用。
5.2 选型时真正该看的几个指标
选框架别只看吞吐数字,我建议重点看这几个方面:
首 token 延迟(TTFT)和每 token 延迟(TPOT)要分开看。TTFT 决定用户多久能看到第一个字,TPOT 决定后续生成的速度。不同框架在这两个指标上的表现可能差异很大,要结合你的场景判断哪个更重要。
显存效率直接关系到你的硬件成本。同样的卡能跑多大的模型、支撑多少并发,这是真金白银的差别。
对量化的支持程度也很关键。有些框架对某些量化格式支持好,有些则有限制,选之前要确认清楚。
社区活跃度和文档质量决定了你遇到问题能不能快速找到答案。一个再快但没人维护的框架,长期来看是负担。
和现有技术栈的兼容性也不能忽略。如果你的服务已经有一套成熟的部署体系,选一个能平滑接入的框架,比推倒重来划算得多。
5.3 从零搭一套推理服务的完整链路
训练营里会带着大家从零搭一套完整的推理服务,这里把链路梳理一下,让你有个全局认识。
第一步是环境准备,包括驱动、CUDA、Python 环境、框架安装。这一步看似简单,实则坑最多,版本不匹配导致的各种报错能折腾很久。建议用容器化方案把环境固化下来,避免"在我机器上能跑"的问题。
第二步是模型准备,包括下载权重、格式转换、量化处理。如果要用量化,这一步就要把量化做好并验证精度。
第三步是服务配置,设置批大小、最大序列长度、显存占用比例、调度参数等。这些参数直接决定性能表现,需要根据实测反复调整。
第四步是接口封装,把推理服务包装成 HTTP 或 gRPC 接口,加上鉴权、限流、日志、监控。这一步是把"能跑"变成"能对外服务"的关键。
第五步是压测调优,用真实或模拟的负载压测,找出瓶颈,针对性优化。这一步往往要迭代好几轮。
第六步是上线监控,持续观察延迟、吞吐、错误率、显存占用等指标,及时发现和解决问题。
6. 那些只有踩过才知道的坑
理论讲再多,不如把实际踩过的坑摆出来。这部分是训练营里最受欢迎的内容,因为都是真金白银换来的经验。
6.1 显存明明够却 OOM 的几种原因
最常见的一种情况是:模型加载完看着还剩不少显存,一跑就 OOM。原因通常有几个。一是KV 缓存没算够,前面说过它随序列长度和并发增长,很容易被低估。二是显存碎片,反复分配释放之后,虽然总空闲显存够,但没有一块连续的大空间能满足需求。三是框架的显存预分配策略,有些框架会一次性占掉很大比例显存,留给其他操作的空间就少了。四是中间激活值在特定输入下暴涨,比如遇到超长序列时。
排查这类问题,建议先把最大序列长度和批大小调小,确认能稳定运行后再逐步往上加,找到真正的上限。同时用工具监控显存的实际使用曲线,看是哪个环节在涨。
6.2 量化后效果变差的排查思路
量化后效果变差,先别急着否定量化,按顺序排查。首先确认量化过程本身有没有出错,比如校准数据是否合适、分组配置是否合理。然后对比不同量化位宽,4bit 不行就试 8bit,看是不是位宽太低导致的。接着检查是不是特定任务受影响,有些任务对精度敏感,有些则无所谓,可能只需要对敏感任务用高精度。最后考虑混合方案,比如关键层用高精度、其他层用低精度。
我遇到过一个案例:模型量化后通用对话没问题,但一到数学计算就出错。后来发现是量化影响了数值敏感的注意力计算,最后对那几层单独保留高精度才解决。
6.3 并发上不去、延迟忽高忽低的定位方法
并发上不去、延迟不稳定,通常和调度、缓存、资源竞争有关。先看是不是批处理没生效,如果框架配置不对,请求可能还是一个个串行处理的。再看KV 缓存是不是频繁换入换出,如果显存不够导致缓存被反复驱逐,性能会剧烈波动。还要看是不是有长请求拖累短请求,一个超长生成任务占着资源不放,会让其他请求排队。
定位这类问题,压测工具和监控指标是必须的。记录每个请求的 TTFT、TPOT、总耗时,看分布而不是只看平均值,往往能发现被平均掩盖的问题。
6.4 长上下文场景下的特殊处理
长上下文是现在的热点,但也是性能杀手。序列一长,Prefill 计算量暴涨,KV 缓存也膨胀得厉害。针对这种场景,有几个思路:一是用更高效的注意力实现,比如 FlashAttention 这类,能显著降低长序列的显存和计算开销;二是做前缀缓存,如果多个请求共享相同的前缀(比如系统提示词),可以缓存这部分的计算结果,避免重复计算;三是考虑上下文压缩或检索增强,不是所有场景都需要把全部上下文塞进去,用检索的方式只取相关部分,能大幅降低序列长度。
7. 从训练营到生产:把优化落到你的业务里
学完这些技术,最终要落到自己的业务上。这里给几条实操建议。
先建立基线,再谈优化。别一上来就堆技术,先用最简单的配置把服务跑起来,测出基线性能,记录延迟、吞吐、显存占用。有了基线,后面每做一项优化都能量化对比,知道到底有没有用。
一次只改一个变量。优化手段之间会互相影响,同时改好几个,出了问题根本不知道是哪个导致的。改一个、测一个、记录一个,稳扎稳打。
性能优化没有终点,只有平衡。你的业务在变、流量在变、模型在变,今天的最优配置明天可能就不适用了。建立一套持续监控和调优的机制,比一次性调好更重要。
别忽略成本和可维护性。有时候为了榨出最后一点性能,引入的复杂度会让后续维护成本飙升,得不偿失。够用就好,把精力留给真正影响业务的地方。
训练营的价值不在于教会你几个命令,而在于帮你建立起一套分析问题、定位瓶颈、选择方案、验证效果的完整方法论。这套方法论,才是你在面对任何新模型、新硬件、新场景时都能用得上的东西。