
1. AI 系统性能工程到底在解决什么问题1.1 从一个真实场景说起去年我接手过一个推理服务的优化项目模型本身只有 7B 参数单次推理在开发机上跑下来也就几百毫秒团队里所有人都觉得“这玩意儿上线肯定没问题”。结果真到了生产环境QPS 一上来P99 延迟直接飙到 8 秒以上GPU 利用率却只有 30% 出头。这个反差特别典型——模型能跑通和模型能扛住生产流量完全是两码事。这就是 AI 系统性能工程要处理的核心矛盾算法层面的指标准确率、BLEU、F1和系统层面的指标吞吐、延迟、成本、稳定性之间存在巨大的鸿沟。传统后端性能工程关注的是 CPU、内存、IO、网络这些通用资源而 AI 系统多了一层——GPU/NPU 这类加速器以及模型推理/训练特有的计算图、显存、批处理、算子融合等维度。你不懂这层优化就无从下手。我写这个系列是想把过去几年在推理服务、训练集群、Agent 编排系统上踩过的坑和总结的方法论系统性地梳理一遍。适合谁看如果你是把模型从 notebook 推到生产的算法工程师或者是被拉来给 AI 服务做性能保障的后端/运维同学再或者是在做 AI 基础设施选型的技术负责人这个系列应该都能给你一些直接能用的东西。1.2 性能工程和“调参”的本质区别很多人把性能优化理解成“调调 batch size、换个大点的显卡”这是把性能工程做成了玄学。真正的性能工程是一套可测量、可归因、可复现的方法论。我习惯把它拆成四步建立基线在受控条件下测出当前系统的真实表现包括吞吐、延迟分布不只是平均值P50/P95/P99 都要看、资源利用率、成本。定位瓶颈用 profiling 工具找到真正的瓶颈在哪一层——是计算密集、访存密集、通信密集还是调度/框架开销。提出假设并验证每次只改一个变量看指标怎么动。改两个变量一起上你永远不知道是哪个起了作用。回归与固化优化后的配置要能稳定复现写进部署脚本加进监控告警。这四步听起来朴素但我在实际项目里见过太多团队跳过第一步和第三步直接凭感觉改配置最后性能是上去了但没人说得清为什么下次遇到类似问题还是抓瞎。提示性能工程里最贵的成本不是显卡是“不知道瓶颈在哪就乱改”浪费的人天。先测量再动手。1.3 为什么现在必须重视这件事过去两年 AI 应用的形态发生了明显变化。以前大家关心的是“能不能训出一个效果好的模型”现在关心的是“这个模型能不能在可接受的成本下服务百万级用户”。大模型推理的单次成本比传统模型高出一到两个数量级一个没优化好的推理服务账单可能是优化后的五到十倍。同时Agent 类应用把单次请求变成了多轮模型调用加工具调用的链路延迟是累加的任何一个环节慢一点端到端体验就崩了。所以性能工程不再是“上线前临时抱佛脚”的环节而是从架构设计阶段就要介入的一等公民。你选什么推理框架、用什么量化方案、批处理策略怎么设计、缓存放在哪一层这些决策在写第一行代码之前就应该有性能视角的考量。2. 核心指标体系先把“好”定义清楚2.1 延迟指标别只看平均值延迟是用户最直观的感受但“平均延迟 200ms”这句话几乎没有信息量。真正有意义的是延迟分布。我一般会关注这几个分位数指标含义典型关注场景P50中位数一半请求快于此整体体验基线P9090% 请求快于此常规 SLAP9595% 请求快于此较严格 SLAP9999% 请求快于此长尾体验、超时设置依据P99999.9% 请求快于此极端长尾金融/实时场景为什么 P99 比平均值重要因为平均值会被大量快请求拉低掩盖掉那 1% 的慢请求。而恰恰是这 1% 的慢请求决定了你的超时阈值设多少、用户会不会投诉、上游服务会不会被拖垮。我见过一个服务平均延迟 150ms 看起来很漂亮但 P99 是 6 秒结果就是每 100 个用户里有一个要等 6 秒投诉率居高不下。另外要区分TTFTTime To First Token和TPOTTime Per Output Token。对于流式输出的对话类应用用户感知的“快”主要是 TTFT——第一个字多快出来。而 TPOT 决定了后续文字吐出的流畅度。这两个指标的优化手段完全不同TTFT 受 prefill 阶段和排队影响大TPOT 受 decode 阶段和显存带宽影响大。2.2 吞吐指标QPS、TPS 和并发数吞吐衡量的是单位时间能处理多少请求。常见的有 QPS每秒查询数、TPS每秒 token 数对大模型更贴切。这里有个容易混淆的点吞吐和延迟通常是矛盾的。你把 batch size 调大吞吐上去了但单个请求的延迟也上去了因为要等凑批。所以不存在“吞吐又高延迟又低”的免费午餐只有根据业务场景找平衡点。并发数则是另一个维度。很多人以为并发数越高吞吐越高实际上并发数超过某个点之后吞吐不升反降因为资源争抢和调度开销开始占主导。这个拐点在哪里必须实测。2.3 资源利用率GPU 到底忙不忙GPU 利用率是个被严重误读的指标。nvidia-smi显示的 GPU-Util 是“在采样周期内有 kernel 在执行的百分比”它高不代表计算单元真的在满负荷干活。一个访存密集的 kernel 可以让 GPU-Util 跑到 100%但 SM 的计算单元利用率可能只有 20%。所以我更关注这几个SM 占用率 / 计算吞吐用 Nsight Compute 看反映计算单元真实忙碌程度。显存带宽利用率大模型 decode 阶段通常是访存瓶颈这个指标比算力利用率更关键。显存占用包括模型权重、KV Cache、激活值。KV Cache 在大 batch 长上下文下会吃掉大量显存是 OOM 的常见元凶。SM 活跃 warp 数反映延迟隐藏能力太低说明并行度不够。2.4 成本指标每百万 token 多少钱技术指标最终要落到钱上。我习惯算一个单位成本每处理一百万 token 需要多少 GPU 小时折算成电费加折旧。这个数字能直接和业务收入对比判断这个服务是否可持续。优化性能的终极目标很多时候就是把单位成本压到业务可承受的范围内。3. 瓶颈定位性能工程的手艺活3.1 先分层再定位AI 系统的性能问题可能出在很多层盲目优化等于大海捞针。我习惯按这个层次从上往下排查业务/调度层请求排队、批处理策略、路由、重试逻辑。服务框架层推理框架如各类 serving 框架的调度、内存管理、并发模型。模型执行层计算图、算子、kernel 实现、量化。硬件层GPU/NPU 的计算、显存、互联带宽。一个经验法则是先看上层再看下层。因为上层的问题往往更容易修收益也更直接。比如一个服务 P99 高可能根本不是模型慢而是排队策略有问题——请求全挤在一个队列里前面的大请求把后面的小请求堵死了。这种问题改调度策略就能解决根本不用碰模型。3.2 Profiling 工具怎么选不同层用不同工具我整理了一张常用工具对照表层次工具主要用途服务框架框架自带 metrics、Prometheus请求延迟分布、队列长度、批大小模型执行PyTorch Profiler、Nsight Systems算子耗时、kernel 时间线、CPU-GPU 同步Kernel 级Nsight Compute单 kernel 的计算/访存瓶颈分析硬件nvidia-smi、DCGM利用率、显存、功耗、温度端到端分布式追踪跨服务链路延迟归因我个人的习惯是先用框架 metrics 和分布式追踪定位到“慢在哪个环节”再用 Nsight Systems 看这个环节内部的时间线最后如果发现是某个 kernel 慢才上 Nsight Compute 深挖。不要一上来就 Nsight Compute那个信息量太大容易迷失。3.3 一个真实的定位案例之前有个推理服务P99 延迟忽高忽低波动很大。我先看框架 metrics发现队列长度在波动说明请求到达和处理速度不匹配。再看 Nsight Systems 的时间线发现 GPU 上有明显的“空洞”——kernel 之间有几百毫秒的空隙GPU 在等 CPU。顺着这个线索查下去发现是 tokenizer 在 CPU 上串行执行成了瓶颈。大 batch 下 tokenizer 处理时间线性增长GPU 只能干等。解决方案是把 tokenizer 并行化并且和 GPU 计算流水线化。改完之后 P99 从 3 秒降到 800ms。这个案例的教训是GPU 服务的瓶颈经常不在 GPU 上。CPU 侧的预处理、后处理、调度逻辑都可能是隐藏的瓶颈。只看 GPU 利用率会误导你。提示看到 GPU 利用率不高先别急着说“GPU 没吃满”要问“GPU 在等什么”。等待的原因往往在 CPU 侧或 IO 侧。4. 优化手段从能用到好用4.1 批处理最有效的杠杆之一批处理是提升吞吐最直接的手段。原理很简单GPU 擅长并行计算一次处理 32 个请求和一次处理 1 个请求耗时可能只差一点点但吞吐差了 32 倍。但批处理有几个坑静态批 vs 连续批静态批要等凑齐一批才处理延迟高连续批continuous batching是动态地把新请求塞进正在执行的批次延迟和吞吐兼顾。现在主流推理框架基本都支持连续批选型时一定要确认。批大小不是越大越好超过某个点显存不够尤其 KV Cache或者计算变成访存瓶颈吞吐不再增长。这个最优点要实测。请求长度差异大时长请求和短请求混在一起短请求会被长请求拖累。可以考虑按长度分桶或者用 chunked prefill 把长请求切开。我实测过一个 13B 模型的服务batch size 从 1 加到 16吞吐涨了约 10 倍延迟只涨了不到 2 倍。但从 16 加到 32吞吐只涨了 20%延迟却翻倍了。所以 16 就是这个场景的甜点。4.2 量化用精度换性能量化是把模型权重和激活值从高精度如 FP16降到低精度如 INT8、INT4减少显存占用和计算量。常见方案训练后量化PTQ不需要重新训练直接对训练好的模型量化。简单快速但精度损失可能较大。量化感知训练QAT训练时就模拟量化误差精度保持更好但需要训练资源。权重量化 vs 激活量化只量化权重实现简单收益主要是显存同时量化激活收益更大但实现复杂。量化的收益很直接INT8 相比 FP16显存减半理论算力翻倍。但实际收益取决于硬件是否支持低精度加速以及 kernel 实现质量。有些场景下量化后反而变慢因为要插入反量化操作。所以量化一定要实测不能想当然。4.3 KV Cache 优化大模型推理的显存大户自回归生成时每个 token 都要和之前所有 token 做注意力计算。为了避免重复计算会把之前 token 的 Key 和 Value 缓存起来这就是 KV Cache。它的显存占用是KV Cache 大小 2 × 层数 × 注意力头数 × 头维度 × 序列长度 × batch size × 精度字节数以一个 13B 模型为例假设 40 层、40 头、头维度 128、FP16序列长度 2048batch size 162 × 40 × 40 × 128 × 2048 × 16 × 2 字节 ≈ 21.5 GB光 KV Cache 就 21.5 GB加上模型权重 26 GB一张 48GB 的卡基本就满了。所以 KV Cache 优化是大模型推理的关键PagedAttention把 KV Cache 分页管理减少碎片提升显存利用率。KV Cache 量化把 KV Cache 也量化到 INT8显存减半。滑动窗口 / 稀疏注意力只保留最近 N 个 token 的 KV长上下文场景下大幅省显存。前缀共享多个请求共享相同前缀如 system prompt的 KV Cache。4.4 算子融合与计算图优化深度学习框架执行模型时是一堆算子按顺序跑。每个算子都有 kernel 启动开销和访存开销。算子融合就是把多个算子合并成一个 kernel减少启动次数和中间结果的访存。比如LayerNorm Linear GELU这三个算子如果不融合要写三次中间结果到显存再读回来融合成一个 kernel中间结果留在寄存器里访存大幅减少。这类优化通常由推理框架或编译器自动完成如 TensorRT、TorchScript、各类图编译器但你需要知道它存在并且在选型时关注框架的融合能力。4.5 并行策略单卡不够就上多卡模型大到单卡放不下或者单卡吞吐不够就得上多卡。常见并行方式数据并行每张卡一份完整模型处理不同数据。实现简单但显存不省。张量并行把单个算子切到多卡层内并行。通信频繁对互联带宽要求高。流水线并行把不同层放到不同卡层间并行。会有流水线气泡需要调度优化。专家并行MoE 模型专用不同专家放不同卡。选哪种取决于模型大小、卡间带宽、延迟要求。张量并行适合单机多卡NVLink 带宽高流水线并行适合跨机。实际生产里经常是几种并行混合使用。5. 常见问题与排查速查5.1 典型问题速查表现象可能原因排查方向GPU 利用率低CPU 预处理瓶颈、IO 等待、批太小看 CPU 占用、Nsight 时间线空洞P99 延迟高但 P50 正常长请求拖累、GC、显存碎片按请求长度分组统计、看显存分配吞吐上不去批处理策略差、显存带宽瓶颈调 batch、看带宽利用率OOMKV Cache 过大、batch 太大、碎片算 KV Cache 大小、开 PagedAttention多卡扩展性差通信瓶颈、负载不均看 NCCL 通信时间、各卡利用率量化后变慢硬件不支持、反量化开销对比量化前后 kernel 时间5.2 几个容易踩的坑坑一只看平均值。前面说过平均值会骗人。一定要看分位数尤其是 P99。坑二忽略冷启动。服务刚启动时模型加载、CUDA 上下文初始化、kernel 编译如果是 JIT都要时间。如果不做预热第一批请求会特别慢。生产环境一定要有 warmup 流程。坑三显存碎片。长时间运行的服务显存分配释放频繁容易产生碎片导致明明总显存够却分配失败。用显存池或 PagedAttention 能缓解。坑四盲目追新框架。新框架可能性能好但稳定性和生态成熟度要评估。生产环境选型稳定优先。坑五不做压测就上线。开发环境的流量模式和生产的完全不同。上线前一定要用接近真实的流量做压测包括请求长度分布、并发模式。5.3 压测怎么做才靠谱压测不是简单地用工具打流量。我一般会构造真实流量分布请求长度、并发模式要贴近生产。用固定长度的请求压测结果没有参考价值。逐步加压从低并发开始逐步加观察指标拐点。找到吞吐不再增长、延迟开始飙升的那个点。持续足够长时间短时间压测发现不了显存泄漏、碎片累积这类问题。至少跑几小时。监控全链路不只监控服务本身还要看下游依赖、网络、存储。记录配置每次压测的配置、代码版本、环境都要记录否则结果无法复现。6. 从性能工程到成本工程6.1 算清楚每百万 token 的成本性能优化的最终目的是降本增效。我习惯把性能指标折算成钱每百万 token 成本 (GPU 小时单价 × 处理百万 token 所需 GPU 小时)假设一张卡每小时 10 元吞吐是每秒 2000 token那么处理一百万 token 需要1,000,000 / 2000 500 秒 ≈ 0.139 小时 成本 10 × 0.139 ≈ 1.39 元这个数字能直接和业务收入对比。如果每百万 token 收费 5 元毛利率就是 72%。优化性能就是提升这个毛利率。6.2 性能优化的优先级排序资源有限优化要排优先级。我的排序原则先修正确性问题如果服务不稳定、会 OOM、会超时先修这些性能其次。再修高收益低成本的比如调 batch size、开连续批、加缓存改动小收益大。然后修高收益高成本的比如量化、换框架、上多卡需要投入但收益可观。最后修低收益的微调 kernel、换更快的算子收益有限投入产出比低。不要一上来就啃最难的骨头先把容易的收益拿到手。6.3 建立持续性能监控性能工程不是一次性的项目是持续的过程。模型更新、流量变化、依赖升级都可能让性能退化。所以要建立持续监控核心指标看板吞吐、延迟分位数、资源利用率、成本实时展示。性能回归测试每次发版前跑一遍标准压测对比基线退化超过阈值就拦截。告警P99 超过阈值、GPU 利用率异常、成本突增都要告警。我在实际项目里的体会是性能问题往往是“温水煮青蛙”——不是某次改动突然变慢而是每次改动慢一点点累积起来就崩了。持续监控能在早期发现这种退化。最后分享一个我常用的技巧给每个服务维护一份“性能档案”记录它的基线指标、瓶颈点、优化历史、当前配置。新人接手时看这份档案能快速理解这个服务的性能特征少走很多弯路。这份档案不需要多正式一个 Markdown 文件就够关键是要持续更新。