大模型推理性能调优实战:从量化到KV Cache的系统优化指南

发布时间:2026/10/19 10:47:45
大模型推理性能调优实战:从量化到KV Cache的系统优化指南 把模型训练得再漂亮如果推理阶段跑不动线上照样一团乱。我做过几个涉及LLM和图像模型的部署项目最深的感受是模型的推理性能调优很多时候比训练更考验人的工程判断力。同一个模型在不同框架、不同并发、不同量化策略下延迟和吞吐可能差出好几倍。这种差距不是靠换一块更贵的显卡就能抹平的而是要靠系统性地拆解瓶颈、逐层优化。这篇文章就是想把我在实际部署里用到的调优思路、踩过的坑、以及最后沉淀下来的建议整理出来。内容不绕弯子先从指标聊起再拆模型层、框架层、服务层最后附上一份常见问题的排查记录。无论你是刚把模型跑通的新手还是已经在线上扛过QPS的开发者里面应该都有能直接用的东西。1. 先聊清楚推理性能调优到底在调什么1.1 为什么推理阶段的“快”和“省”同样重要很多人一开始做推理优化目标只有一个让单次请求变快。这个想法没错但太片面了。我曾经接手过一个在线问答服务单看一次请求的时延已经压到了200毫秒以内看起来很不错结果一旦并发上来GPU利用率拉满新请求排队P95时延直接飙到1.5秒用户体验直线下降。真正的问题在于推理性能不是一个单点指标而是一组互相制约的指标。你在降低单次延迟的时候可能会牺牲吞吐你在提升吞吐的时候可能会放大波动你想压显存就可能降低精度。所以调优的第一步不是急着改代码而是先把性能现状测量清楚明确当前业务最需要的是什么。是首字快还是整体吞吐高还是显存占用小这个优先级决定了后面所有技术选型。1.2 用一组指标把性能问题说清楚我每次做推理性能评估都会先固定一套指标口径。没有统一口径前面测的数据和后面测的数据根本没法对比。常用的有这么几个。端到端延迟从客户端发起请求到收到完整响应的总时间。注意要分P50、P95、P99看只看平均值的意义不大。TTFT首Token延迟从请求发出到收到第一个输出Token的时间。对话和流式场景尤其看重这个指标用户能感知到的“卡不卡”其实大部分来自TTFT。TPOTToken间延迟每生成一个Token的耗时。它决定了一段回答的后半段有多流畅。吞吐量单位时间内处理的请求数或者更细一点每秒生成的Token数。批处理场景最关心这个。显存占用包括模型权重、KV Cache、激活值、框架运行时开销。它直接决定了你能在多大规模的卡上跑多长的上下文。这些指标之间的关系可以类比成一条马路延迟是单辆车通过的时间吞吐是单位时间通过的车流量显存是路的宽度。你要让一辆车跑得快可以给它清空车道但总吞吐就下来了要让车流量大就必须接受单车时间变长。推理调优的本质就是在这几个指标之间找平衡点。1.3 记一次性能摸底的真实记录给一个具体的例子。我之前做过一个文档智能问答项目模型是开源7B规模部署在两块24G显卡上输入是用户上传的PDF解析文本输出是300字左右的答案。第一次做压测的时候结果很吓人单并发情况下TTFT大约300毫秒TPOT约80毫秒端到端3秒出头看起来勉强能用。但把并发提到8个之后TTFT直接跳到1.2秒TPOT变成200毫秒而且显卡利用率只有50%出头另外一半时间像是凭空消失了。把这个现象拆开看问题出在几个地方请求没有做动态批处理每个请求各跑各的GPU的并行能力没被利用起来预处理阶段的分词和向量检索在CPU上跑成了隐藏瓶颈模型权重是FP16显存里塞了很大一份留给KV Cache的空间就变小了。这一轮摸底做完后面的优化方向就非常明确了先解决框架选型和批处理再考虑量化和显存规划。2. 模型层面的三板斧量化、裁剪与编译2.1 量化用精度换吞吐量化是推理优化里见效最快、门槛也相对较低的一步。原理很简单模型权重默认是FP16占用显存大乘法运算也慢把权重从16位降到8位甚至4位之后显存占用直接减半甚至减到四分之一计算速度也会因为访存压力变小而明显提升。我刚才提到的那个7B模型FP16权重大约占14G显存。转成INT8之后变成7G转成INT4的话只要3.5G。但这不等于说你可以直接无脑上最低精度。实际操作中我建议按这个顺序来做先用PTQ训练后量化试INT8这是一种不需要重新训练模型的方式只需要少量校准数据来统计激活值的分布确定量化参数。如果INT8精度损失可以接受再试INT4。但要注意7B以下的小模型INT4之后回答质量下降会比较明显尤其是在数学推理和代码生成这类任务上。如果精度无法满足业务要求再考虑QAT量化感知训练。这种方法在训练过程中就模拟量化的误差让模型自己适应低精度表示精度保留效果更好但需要额外的训练资源和数据准备。另外一个容易被忽略的点是激活值量化。权重量化能省显存但如果激活值还是FP16计算过程中仍然要频繁做高精度浮点运算延迟收益有限。大多数推理框架都支持动态量化就是权重和激活一起量化建议优先开启。我实测下来8位权重加8位激活的组合在很多主流GPU上都有接近两倍的性能提升。注意量化并不是一些零散小技巧的堆砌而且需要验证。改完量化之后一定要准备一组和线上业务分布一致的数据做精度对比不要只看跑通没跑通。曾有一次我把一个分类模型压到INT4之后单条延迟从30毫秒降到12毫秒但准确率从98.5%掉到92%这种代价对有些业务是完全不能接受的。2.2 KV Cache 与注意力机制优化聊到生成式模型就绕不开KV Cache。这个技术简单说就是在生成下一个Token时不必重新计算所有历史Token的Key和Value矩阵而是把之前算好的结果缓存起来复用。它是自回归解码能跑快的基础但同时也是显存消耗的大头。KV Cache的占用可以用一个公式估算总显存 批量大小 × 序列长度 × 层数 × 2 × 注意力头维度 × 单元素字节数我用一个实际例子算一下。假设模型有32层注意力头维度是128批量大小设为8上下文长度设为4096使用FP16存储。单层的KV Cache大小是 8×4096×2×128×2字节约16MB。32层下来就是512MB。如果上下文长度翻倍到8192KV Cache就要1G以上。所以那些宣称支持超长上下文的模型如果不优化KV Cache显存很快会被吃掉。针对这个问题有两类优化思路。第一类是架构层面的比如分组查询注意力GQA。它让多个注意力头共享一组Key和Value直接减少缓存体积。第二类是存储和调度层面的比如分页注意力PagedAttention它像操作系统的虚拟内存一样把KV Cache切分成小块按需分配避免连续内存造成的大量碎片浪费。大部分主流推理框架都已经内置了这两类支持你要做的就是在配置里正确开启并且根据上下文长度和并发放大招调整KV Cache的上限。2.3 算子融合、图优化与编译型方案Transformer模型的网络结构很规整基本都是矩阵乘法、LayerNorm、激活函数、残差连接这些操作的组合。如果每个算子都单独启动一个GPU内核大量时间会花在内核启动和数据搬运上而不是实际计算上。这就是算子融合的价值所在——把多个逻辑上连续的操作合并成一个内核。举个例子LayerNorm Resid Add这三个操作在PyTorch里是分开跑的但它们在计算图上完全相邻完全可以合并成一个操作。一次内核启动就能完成少了两轮显存读写。这类优化在深度学习中一般被称为图优化很多框架会自动做但自动做的覆盖率和手工调整的精细程度还是有差距。编译型方案是更彻底的路径。简单理解就是把模型的计算图转换成针对特定显卡高度优化的指令序列并且在这个过程中做算子融合、显存规划、自动调优。这类方案的代表包括TensorRT、TensorRT-LLM以及一些框架自带的编译后端。我在实践中比较推荐的一个组合是动态图框架负责日常开发和实验等模型结构稳定了再导出成编译型方案做线上部署。用动态图直接上线虽然开发体验好但性能往往要打不少折扣用编译型方案上线开发阶段麻烦一些但能压榨出接近硬件极限的性能尤其在延迟敏感的场景里这个收益非常值得。3. 推理框架与运行时的选型心得3.1 几类主流框架的定位对比框架选型是推理调优里最关键的一步选错了后面怎么调都费劲。市面上的推理框架大致可以分成三大类各有各的定位整理成表格看得更清楚。类别代表方案核心特点适合场景动态图直出基于PyTorch的原生部署开发快灵活易于调试原型验证、低频内部服务批量调度框架vLLM、TGI、SGLang内置连续批处理、KV Cache管理高并发LLM服务编译型方案TensorRT-LLM、ONNX Runtime极致延迟、算子级优化延迟敏感的固定模型动态图方案不是不能用但如果你的服务要面对每秒几十个请求我强烈建议换成批量调度框架。这类框架的核心能力是连续批处理Continuous Batching它不会等一个批次里所有序列都生成完毕才开始下一个批次而是让先完成的请求立刻腾出位置让新请求插入进来。这样做能把GPU利用率从50%拉到90%以上吞吐提升非常明显。3.2 分场景的选型建议我的选型逻辑一般是这样。如果模型还在频繁迭代隔几天就换个版本那就不要过早绑定编译型方案先用批量调度框架跑起来因为编译一次可能要花几十分钟甚至更久迭代成本太高。如果模型结构已经冻结服务要长期运行并且对P99延迟要求很高那就花时间做编译型方案的接入。如果目标是跨平台部署比如同时跑GPU和CPU那么ONNX Runtime是兼容性最好的选择。另外要注意一个现实问题框架本身的版本更新很快接口变动也频繁。我见过不少项目因为在某个版本上绑定了自定义算子导致后来框架升级的时候直接卡住不敢动也不能动。我的建议是线上部署尽量使用稳定版本自定义代码不要侵入框架内部优先级最高的是保留一个干净的上游依赖。3.3 不要忽略预处理和后处理环节很多人调了半天模型性能结果发现瓶颈根本不在模型本身。我之前有个图像分类服务模型推理只要8毫秒但端到端却要150毫秒查了半天才发现问题在图像解码上。图片传进来要先解码成原始像素这个过程在CPU上跑一张大图要一百多毫秒把模型省下的时间全部吃掉了。LLM场景也一样分词、文本向量化、检索、JSON解析、日志序列化这些环节都可能成为隐形瓶颈。我的建议是在做性能剖析的时候一定要把整个请求链路拆开计时不要只盯着模型那一段。一般可以用OpenTelemetry这类工具给每个环节打点先看数据再下结论。很多所谓“推理慢”的问题最后都改成了缓存、并发和序列化优化模型根本不用动。4. 部署时的服务层调优4.1 并发、批处理与排队策略GPU是典型的“吃并发”设备单请求跑一个batch利用率很低。想让吞吐上去就得让多个请求同时到达、合并成一个大batch一起计算。但这里面有个度的问题。batch太大单个请求的等待时间就会变长TTFT会显著上升。所以不能一味加大batch而是要让批处理自适应流量的波动。落地的时候我通常会设置两个上限一个是最长等待时间比如25毫秒一个是最大批量大小比如16。当队列里攒了一批请求或者等了足够长时间就触发一次推理。这样在低峰期不会因为死等而增加延迟高峰期也不会因为batch太大而让单请求卡死。排队策略同样不能忽视。如果服务端没有队列控制突发的并发请求会把GPU计算队列打满形成“请求风暴”表现为所有请求一起变慢。我常用的做法是给每个GPU副本设置一个最大并发上限超过上限的请求直接返回503或者进入客户端侧的退避重试而不是无限堆积。4.2 显存与KV Cache的容量估算显存规划是整个部署方案里最需要精打细算的一步而且必须在买卡之前就算清楚否则很容易出现“看起来够用一上线就爆”的尴尬局面。推理时的显存占用主要包括四块模型权重、KV Cache、激活值/临时缓冲区、框架运行时开销。以7B模型为例FP16权重约14G激活值大概2~4G运行时预设几百兆到1G。剩下的空间才留给KV Cache。上一节已经给过KV Cache的计算公式这里再补一句如果你的批量大小是8上下文长度是4096KV Cache要500多M那么总显存至少是 14 4 1 0.6 ≈ 19.6G在24G的显卡上能跑但余量不算太宽裕。如果打算支持并发32路、上下文8192那KV Cache轻松超过4G显存规格就得重新考虑。我在这里吃过一次亏。某个项目上线前估算显存时忘了把多个副本的显存占用算进去以为一张卡能跑两个实例结果启动第二个实例时直接OOM线上服务中断了十几分钟。现在我的习惯是显存估算一律按峰值来并且要额外预留20%的冗余空间宁可使用率低一点也不要在高峰期去赌那个临界值。4.3 预热、超时与重试稳定性才是第一指标很多推理服务在刚启动的时候特别慢第一个请求要等好几秒后面的请求却只要几百毫秒。这不是模型问题而是框架在第一次推理时要做大量初始化加载CUDA内核、编译计算图、分配显存、填充缓存。解决这类问题最直接的办法是预热Warmup。在服务启动完成后模拟发送几条典型的请求数据让模型跑一遍确保所有懒加载的组件都被激活。超时与重试策略更需要面向业务侧来设计。AI推理有一个特点就是服务端不一定能在指定时间内返回结果尤其是生成式模型长文档或复杂推理请求可能耗时几十秒。如果客户端设置了过短的超时会导致大量请求被误判为失败。我的建议是超时时间至少覆盖训练数据里P99的耗时并且超时之后要有退避重试机制不要马上把所有请求都打进重试队列否则雪崩效应迟早会出现。我见过的最惨烈的线上事故就是重试风暴把本来还能扛住的服务直接打垮。后来加了随机退避和最大重试次数限制系统才恢复稳定。5. 常见问题与排查工具5.1 时延毛刺的定位思路线上服务最让人头疼的不是平均延迟高而是平均延迟很低但时不时冒出一个超长延迟的毛刺。用户能感知到的卡顿往往都由这些长尾延迟引起。解决毛刺问题时我一般的排查顺序如下。先看基础设施层CPU有没有被其他进程抢占、GPU有没有被别的任务分走、网卡和磁盘I/O是否正常。再看服务层有没有持久化操作混在请求链路里、日志写入和指标上报是否阻塞了主流程、内存回收频率是否过高。最后才回到模型层当前是不是有大batch在跑导致后续请求排队或者某个请求生成了特别长的序列。常用工具无非是nvidia-smi看GPU占用、perf查内核态耗时、链路追踪看每个Span的耗时分布、Grafana盯P99趋势。有一件事我很推荐在压测阶段不要只看平均时延专门盯最高延迟那1%的请求。如果它们总是出现在相同的时间点比如整点或服务重启后的前几分钟那大概率是定时任务或缓存冷启动导致的。5.2 显存OOM的排查显存不够用的时候最容易犯的错误是只盯着进程消失前的最后状态然后无脑把最大序列长度调小。这个方向不一定对因为有时候问题不是单请求太长而是并发请求太多KV Cache总量超过了上限。正确做法是先把显存账算清楚。用nvidia-smi查看GPU当前占用再用框架自带的显存分析工具看权重和缓存各占多少。如果是权重占了绝大部分考虑量化或换更小的模型。如果是KV Cache占了大头就要同时降低max_tokens、限制并发数或者开启分页注意力。还有一类很隐蔽的OOM跟显存碎片化有关。服务跑了好几天显存总量明明足够但新请求就是分配不出连续内存块。这种情况通常是连续分配和释放导致的。解决办法是重启服务或者在框架层面开启显存碎片整理功能但根本方案还是要限制长期运行的缓存池大小。5.3 速度始终上不去的几个隐性原因如果所有指标看起来都是正常的但吞吐始终上不去那大概率和模型本身关系不大而是出在工程管线里。我总结过几个常见原因。数据加载慢数据集存在本地磁盘每次推理都要读磁盘I/O成了瓶颈。解决方法很简单把数据预热到内存或SSD上。CPU-GPU搬运频繁预处理在CPU上做结果没复用GPU内存而是每次重新拷贝这个开销在网络输入输出不稳定时会放大。Tokenizer耗时占比高长文本输入的分词操作会比想象中慢很多如果没做缓存每次请求都要重复分词。使用了过旧的推理后端老版本框架对新的硬件特性和算子融合支持有限经常是升级一个主版本就能白拿20%的性能提升。硬件特性没开启比如某些GPU支持低精度加速但在软件层面默认是关闭的需要显式配置。我把这些整理成一个速查表碰到“速度上不去”的时候逐个排查一遍基本能把80%的问题定位出来。排查项现象常用解决手段数据加载磁盘I/O高、CPU等待高数据预加载、内存映射CPU-GPU搬运Host-to-Device拷贝耗时高显存内存复用、异步复制Tokenizer长文本分词耗时大分词结果缓存、升级分词库框架版本老版本无相关优化升级到稳定新版本硬件特性低精度加速未启用检查并开启相关开关在实际操作过程中我最大的感受是推理性能调优不是一个一次性的任务而是一个需要持续观测、持续调整的动态过程。模型会升级、流量会变化、硬件会被换新每一个变量都可能会让之前的优化失效。最合理的做法是建立一套端到端的性能基准每当有新版本、新流量模式或新硬件出来的时候都重新跑一遍压测用数据说话而不是凭感觉决定要不要动配置。最后再分享一个小技巧。每次做优化最好只改一个变量然后保留一组完整可复现的压测数据。比如这次只改量化精度下次只改并发策略这样你才知道真实收益到底来自哪个改动。如果一次改好几个变量性能变好变坏都说不清楚原因后面再想复盘就麻烦了。调优这事情耐心和严谨比花活重要得多。

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询