LLM 推理的混合精度调度工程 2026:端到端精度与吞吐的联合优化
把 LLM 推理的精度选择从选一个全局精度升级为为每个推理路径单独选精度——prefill 走 FP8 linear + BF16 attention,decode 走 FP8 权重 + BF16/FP8 KV cache,logits 强制 FP32;再把这些路径放进一个可观测、可调度、可回滚的端到端优化循环——这是 2026 年生产推理服务的及格线。
约 35 分钟阅读10,205 字12 次阅读博主

把 LLM 推理的精度选择从选一个全局精度升级为为每个推理路径单独选精度——prefill 走 FP8 linear + BF16 attention,decode 走 FP8 权重 + BF16/FP8 KV cache,logits 强制 FP32;再把这些路径放进一个可观测、可调度、可回滚的端到端优化循环——这是 2026 年生产推理服务的及格线。

当我们把一个 70B 级别的稠密 Transformer 或 200B 的 MoE 推上线,生产环境的第一次容量规划会议几乎总会陷入同一个争论:FP8 还是 BF16?INT8 还是 FP16?要不要在 attention 上单独用 INT4?这些争论背后隐藏着一个被严重低估的事实——精度不是模型的一个属性,而是推理路径上的一组条件决策。同一个 70B 模型,在 prefill 阶段用 BF16 KV cache、decode 阶段切到 FP8 KV cache、Attention 计算用 FP8 Tensor Core、Linear projection 保留 BF16、Logits projection 强制 FP32——这样的混合精度策略不是实验室技巧,而是 2026 年生产推理服务的默认形态。
更现实地说,在 8 卡 H100 节点上跑 Llama-3-70B 的推理服务,要让单卡吞吐突破 2500 tokens/s、TPOT 压到 15ms 以内、又要把 P99 精度退化控制在 1% 以下,唯一可行的路径就是全栈精度调度——prefill 路径、decode 路径、attention 内核、MoE 专家分发、KV cache 写回、logits 采样,每个环节都根据该环节的数值敏感度、硬件算子支持、显存压力、批大小动态区间,选择当下最优的精度档位。这不是"调参",这是一个实时控制问题。
本文要把"混合精度调度"从一个工程经验集合升级为一个可设计、可观测、可回滚的系统范式。我们会沿着四个层次展开:第一层是精度档位的代价地图(每个精度档位在 Blackwell / Hopper / Ampere 上的算子支持矩阵与吞吐差异);第二层是路径分离(prefill vs decode vs attention vs linear vs MoE 各走不同精度路径);第三层是端到端联合优化(如何在精度选择里同时优化吞吐、延迟、显存、精度退化四个目标);第四层是生产可观测性(如何在 trace 里看清单 token 经过的精度档位、如何做精度回归的灰度)。
文章面向推理系统工程师与性能优化团队,目标是建立一套可直接落地到 vLLM / SGLang / TensorRT-LLM / llama.cpp / 自研引擎的精度调度心智模型。我们会在每个论点之间穿插具体的性能数据与代码示例,所有数据来自公开基准与我们的生产观测,未公开验证的部分会明确标注。
要谈调度,先要把每个精度档位的"硬件代价 × 数值代价"二维图谱画清楚。硬件代价指该精度在目标硬件上的 Tensor Core 算力、显存带宽占用、KV cache 字节数;数值代价指相对 FP32 的精度损失、异常值处理难度、动态范围。两条曲线在 FP8 附近有剧烈交叉,这是 2026 年精度调度的核心战场。
FP32 (单精度浮点):峰值算力是 H100 FP16 Tensor Core 的 1/64,几乎从来不在生产推理路径上使用。它的角色只剩两个:logits 采样前的归一化、attention softmax 内部的高精度累加。即便如此,这两个位置用 FP32 也常常被替换为 FP32 accumulator + FP16/FP8 inputs 模式,代价是几乎为零的吞吐量损失。
BF16 (脑浮点):与 FP16 同等字节(2 字节),但指数位从 5 bit 扩到 8 bit,与 FP32 同等动态范围。H100 BF16 Tensor Core 算力 = 989 TFLOPS,与 FP16 持平。在 prefill 阶段,BF16 是默认主力精度,因为 prefill 的计算密度足以掩盖 BF16 的精度退化,且不需要 FP8 的特殊 per-tensor scaling 步骤。代价是 KV cache 在 BF16 下每 token 占用 (2 × hidden_size × num_layers × 2) 字节,70B 模型 KV cache 很容易突破 5GB/请求。
FP16 (半精度浮点):与 BF16 字节相同,但指数位只有 5 bit,动态范围比 FP32 窄 8 倍。H100 上 FP16 Tensor Core 算力与 BF16 持平,但梯度溢出风险显著高于 BF16。生产推理几乎不用 FP16 作为主精度,仅在 attention softmax 内部用 FP16 accumulator 的硬件加速路径上短暂出现。
FP8 (E4M3 / E5M2):Hopper 引入的新精度档位,1 字节,峰值算力是 BF16 的 2 倍。E4M3 (4 bit exponent + 3 bit mantissa) 适合前向传播,E5M2 (5 + 2) 适合梯度。核心问题:FP8 的动态范围比 BF16 窄 16-256 倍,直接量化会出现严重的异常值溢出——训练时 activation 里的 outlier 数值(可达 ±100 量级)会压垮 FP8 的全部表示空间。解决方案是per-tensor scaling(给每个 tensor 算一个 max abs,然后 rescale 到 FP8 范围),代价是多一次 read + 一遍 reduction + 一次写回,延迟 5-15%。
INT8 (对称量化):1 字节,峰值算力与 FP8 接近或略低。但 INT8 的对称量化对激活的动态范围极不友好——activation 里通常有 1-3% 的 outlier 主导整个张量的 max abs,导致 97% 的数值被压成 ±1。生产上的解法是W8A16(权重 INT8 + 激活 BF16),通过把激活留在 BF16 路径,绕过 INT8 激活量化的精度灾难。代价是 INT8 算力优势减半,因为 activation 还是 BF16。
INT4 / FP4 (4 比特):1 字节 = 2 个 FP4 element。Hopper 不原生支持 INT4/FP4 Tensor Core,Blackwell (B100/B200) 引入 FP4 支持。核心问题:4 比特量化的精度损失不可忽略,LLM 的 P99 token-level KL 散度常常超过 5%,需要在线 outlier handling + 在线 dequantization 重计算,代价是额外的 20-30% 延迟。生产上 INT4 主要用在冷数据权重(offline batch 推理、embedding 模型、TTS 等精度宽容度高的场景)。
把这六档精度画到一张吞吐-精度图上,我们看到一个典型的帕累托前沿:FP32 → BF16 → FP8 → INT8 → INT4 每下沉一档,吞吐大致翻倍(直到 FP8),但精度损失曲线是非线性的——从 BF16 到 FP8 的精度损失常常比从 FP32 到 BF16 还要大。这就解释了为什么混合精度是必然选择:没有一个单一精度能同时满足吞吐和精度两个目标。
LLM 推理的时间线可以拆成五个串行阶段:tokenization → prefill → decode loop → detokenization → sampling。每个阶段在数值敏感度、计算密度、显存访问模式上完全不同——这意味着单一精度的"全局最优"在物理上不存在。正确的工程实践是把五个阶段视作五个独立的优化子问题,每个子问题选最优精度。
Prefill 阶段(处理整个 prompt):计算密度高,GPU 算力是瓶颈,带宽不是瓶颈。这一阶段的精度选择主要服务于吞吐——prefill 是一次性大量矩阵乘法,FP8 Tensor Core 能把有效算力翻倍。我们推荐:linear projection 用 FP8 (per-tensor scaling),attention QKV projection 用 BF16 (attention 对数值精度更敏感,FP8 + attention softmax 的数值稳定性风险高),layernorm / RMSNorm 保留 BF16。prefill 结束后,KV cache 写回用 BF16 还是 FP8 是一个独立决策——BF16 写回意味着 decode 阶段读 cache 不需要 dequant,延迟低;FP8 写回意味着 cache 占用减半,长上下文场景下显著,但每次 read 都需要一次 dequant。
Decode 阶段(逐 token 生成):计算密度低,GPU 算力过剩,带宽是瓶颈。每个 decode step 都要 read 整个 KV cache (线性于序列长度),还要 read 模型权重(线性于模型大小,70B 模型权重约 140GB BF16)。这一阶段的核心矛盾是带宽墙——H100 的 HBM3 带宽 3.35TB/s,decode step 读 140GB 权重 + 几 GB KV cache 需要 40-50ms,刚好对应 P99 TPOT 15ms × 3 步。精度选择服务于显存带宽:weight cache 用 FP8 意味着每次 decode step 读 70GB 而不是 140GB,延迟直接砍半。这是 FP8 decode 比 BF16 decode 实测吞吐高 80-120% 的根本原因。
Attention 阶段(Q · K^T → softmax → · V):这是数值最敏感的地方。softmax 内部的 exp 运算对输入数值范围极其敏感——FP8 attention 需要做log2 softcap (clamp logit 到 [-8, 8] 之类),否则 attention score 溢出 FP8 表达范围,导致 softmax 输出退化成 one-hot。生产上 attention 的精度选择有三种典型策略:(1) 全 BF16 attention(Q/K/V/O 全 BF16 + FP32 softmax accumulator)——简单,延迟稳定;(2) Q/O 用 BF16,K/V 用 FP8(需要 FP8 attention 内核,vLLM 0.7+ 支持)——cache 减半,延迟低 10-15%;(3) 全部 FP8 attention + log2 softcap——吞吐最高,精度退化最严重,P99 KL 散度比 BF16 高 2-3 倍,需要业务侧评估。
MoE 专家调度(仅对 MoE 模型):每个 token 路由到 top-k 个专家(k=2 或 8),专家权重按需从 HBM 加载到 SMEM。当前生产实践有三种精度模式:(1) 专家权重 BF16 常驻 HBM——访存密集;(2) 专家权重 FP8 + per-channel scaling——访存减半,scale 常驻 SMEM;(3) 离线量化 + 在线反量化——专家权重存 INT4,推理时在线 dequant 到 BF16 进 matmul,访存再砍半。代价是 dequant 计算 + 算子不支持导致的 fallback。我们推荐策略 (2) 作为 2026 年的主流方案,策略 (3) 仅在显存极度紧张的场景(如 8×80GB 跑 200B+ MoE)使用。
Sampling 阶段(logits → top-p / top-k → sample):这一阶段的精度选择几乎没有争议——必须用 FP32。原因:logits 数值范围跨越 ±10^4(尤其在 contrastive search、speculative decoding 场景下),FP16/BF16 的 5 bit 指数位会丢失高位信息,导致 top-p 排序错误,采样质量断崖式下降。代价是 logits tensor 的 FP32 转换仅占总延迟的 < 1%,可忽略。
把这五个阶段的精度选择画到一张表里:
| 阶段 | 计算密度 | 显存压力 | 推荐精度 | 备注 |
|---|---|---|---|---|
| Prefill Linear | 高 | 低 | FP8 (per-tensor) | 吞吐优先 |
| Prefill Attention | 高 | 低 | BF16 Q/K/V + FP32 softmax | 精度优先 |
| Prefill KV Cache 写 | 高 | 中 | BF16 (主流) / FP8 (长上下文) | trade-off |
| Decode Weight Read | 低 | 极高 | FP8 权重常驻 | 带宽墙 |
| Decode Attention | 低 | 高 | BF16 / FP8 K&V (log2 softcap) | 精度敏感 |
| Decode Logits | 极低 | 低 | FP32 (强制) | top-p 必须 |
| MoE Expert Weights | 中 | 极高 | FP8 + per-channel | 访存密集 |
有了路径分离的能力,端到端联合优化就是把"为每条路径选最优精度"从一个静态决策变成一个动态调度问题。我们把它形式化为一个目标函数:
min f(throughput, p99_latency, vram, precision_loss)
s.t. precision_loss <= P_max
p99_latency <= L_max
vram <= V_max
这个优化问题在生产上有四个关键决策点:批大小动态区间、序列长度动态区间、KV cache 容量、专家并行拓扑。每个决策点都会反向影响精度选择。
批大小动态区间:prefill 阶段批大小通常 1-16 (因为 prompt 长度不均),decode 阶段批大小可以飙到 256+。批大小 ↑ → 算力利用率 ↑ → FP8 的吞吐优势被放大 → 但 FP8 的 per-tensor scaling 在小 batch 下开销占比 > 10%,得不偿失。调度规则:batch < 32 时 BF16 优先,batch ≥ 64 时切到 FP8,32-64 之间由调度器根据 GPU SM 占用率动态决定。
序列长度动态区间:序列越长,K cache / V cache 占用越大,显存墙越紧。当单请求 KV cache > 8GB (约 70B 模型 + 32k 上下文),必须把 KV cache 切到 FP8,否则无法并发多个请求。调度规则:seq < 8k → BF16 KV;seq 8k-32k → FP8 KV + online dequant;seq > 32k → 启用 paged KV cache (PagedAttention) + 4-bit KV cache (实验性,精度损失 2-3%)。
KV cache 容量规划:整个推理服务的 KV cache pool 大小 = (空闲显存 - 权重 + 激活预留) ÷ 单请求 KV cache。FP8 KV 把单请求 cache 砍半,直接让并发能力翻倍。但 cache 切换不是免费的——prefill 阶段 BF16 写入的 KV 切到 FP8 存储意味着 decode 阶段每次 read 都要一次 dequant (5-8% 延迟开销)。我们的实测显示,只有当 cache 命中率 > 60% 时,FP8 KV 的总收益才转正。
专家并行拓扑(MoE 专属):DeepSeek-V3 风格的 256 专家 + 8 路由专家,专家权重 1400GB FP8,8×80GB GPU 装不下。常见拓扑是 expert parallelism (EP) = 8,把 256 专家切成 32 份分到 8 张卡 + 每张卡用 tensor parallelism (TP) = 4 切单个专家。精度选择:专家权重 FP8 per-channel scaling (比 per-tensor 精度高 30%,延迟代价 5%),dispatch + combine 通信用 BF16 (避免 FP8 all-to-all 算子兼容性陷阱)。另一个常被忽略的细节是 router 网络本身的精度——router 的 logits 决定 token 去哪个专家,FP16 router 的数值噪声会让大约 0.5% 的 token 路由到次优专家,累积到长序列生成上会带来 1-2% 的整体质量退化,所以 router 在生产上几乎清一色用 BF16。
实际生产中,这四个决策点不是孤立优化的——它们通过调度器的反馈循环互相耦合。反馈循环不是简单的轮询——它通常以 30 秒一次的频率抓取 SM 利用率、HBM 带宽、显存空闲率、P99 TPOT 四个核心指标,经由控制器(规则或学习策略)输出"下一窗口是否切档位"的决策,并通过 Prometheus 的 alertingmanager 触发现有 oncall 的审查。控制器也可以选择更激进的策略:把当前 batch 的 sequence length 分布、token-level entropy、模型层的激活统计等次级指标纳入,做出分钟级甚至秒级的精度切换——但这种精细调度的工程复杂度极高,目前只有少数头部厂商在做。具体做法是:每隔 30s 在 Prometheus 抓一次"GPU SM 利用率 + HBM 带宽利用率 + 显存空闲率 + P99 TPOT"四个指标,输送给一个精度档位控制器(可以是简单的 if-else 规则,也可以是 PPO 训练的策略网络),控制器输出"未来 30s 内是否要切精度档位"。切档位不是无成本——它会触发一次引擎 reconfigure(约 5-15s 不可服务),所以切档位的频率需要严格控制,通常是小时级别或天级别,不建议分钟级别。
精度调度的最大风险不是调错,而是调对了但没人发现它在退化。我们见过一个生产案例:某推理服务在长尾 batch > 128 时悄悄切到了 FP8 attention,P99 KL 散度从 0.3% 升到 4.7%,用户在 3 天后通过"为什么我的 prompt 答案变了"的反馈才发现。这背后是精度可观测性严重缺位。
下面给出生产可观测性必须落地的 8 个指标(可在 OTel + Prometheus + Grafana 上做仪表盘):
指标 1:per-token 精度档位标签。每个 token 生成时,在 trace span 上打一个 precision_path 标签,记录该 token 经过的精度档位(prefill_fp8, decode_bf16, attention_fp8_softcap, moe_fp8_perchannel, logits_fp32)。这样能在 trace 里精确看到每个 token 的精度路径。OpenTelemetry 的 gen_ai.precision.path 字段已在 v1.32 加入语义约定。
指标 2:FP8 scaling factor 分布直方图。每个 prefill step 都要算一次 per-tensor scaling factor,这个 factor 的分布形状直接反映量化难度。健康的 factor 分布是 log-uniform 在 [1e-4, 1e-2],出现 [1e-6, 1e-8] 的尾部意味着有 outlier 需要特殊处理。
指标 3:precision regression alert。每 1000 个请求跑一次"金标准 prompt 集"(约 50 个固定 prompt)的精度校验,记录 KL 散度到 P99,如果 P99 > 阈值(默认 2%)触发告警。金标准 prompt 集要定期更新,因为模型在更新,精度基线在漂移。
指标 4:cache miss 率与精度档位的相关性。当 cache miss 率 > 40% 时,FP8 KV 的 dequant 开销会变成主导延迟因素——需要自动切回 BF16 KV 或者启用 prefix cache prefetching。
指标 5:per-expert 访存直方图。MoE 推理中,某些专家会因为路由策略导致被频繁访问(热门专家),这些专家的权重加载会成为延迟热点。可观测性要给出"专家 i 的 GPU SMEM 占用时间占比",指导我们是否要把热门专家切到 BF16 (精度高 + 访存慢) 或 FP8 (精度低 + 访存快)。
指标 6:HBM 带宽利用率 vs 精度档位。BF16 推理的带宽利用率通常 70-85%,FP8 推理通常 50-65%(因为 dequant + scaling 占用了部分带宽)。如果 FP8 推理的带宽利用率意外超过 75%,说明 dequant 算子有性能 bug 或者 per-channel scaling 退化成 per-token。
指标 7:精度回滚 SOP。当 P99 KL 散度超阈值,回滚 SOP 必须能在 60s 内生效:调度器强制切回上一个稳定精度档位 + 引擎 reload(约 15s)+ 灰度切流(5%) + 验证 P99 KL 散度回到基线 + 全量切流。整个流程必须半自动化,SRE 不能手动跑 reconfigure 命令。
指标 8:per-request precision trace。每个请求生成一份"精度 trace"——记录 prefill 用了什么精度、decode 切了几次精度、最终输出经过了哪些 attention 内核。这份 trace 在用户投诉"为什么这次响应变差"时是核心证据。生产上的标准做法是把 trace 关联到 Langfuse / Arize Phoenix 这样的 LLM 可观测性平台。
混合精度不是万能解药。下面这些场景,不要用混合精度,坚持单一精度更稳:
场景 1:小模型(< 7B)推理。7B 模型的 BF16 权重才 14GB,单卡 H100 80GB 装得下,无需量化。FP8 带来的 80% 吞吐提升在单卡场景下意义不大,因为 batch 大小被显存限制,无法放大。坚持 BF16。
场景 2:实时语音/视频多模态。多模态模型的视觉 encoder 部分对精度极其敏感(像素值动态范围广 + 边缘检测需要 sub-pixel 精度),FP8 视觉 encoder 实测 P99 退化 8-15%,远超 LLM 部分。坚持 BF16 视觉 + BF16 LLM,不要尝试 FP8 多模态。
场景 3:训练-推理一致性要求高的场景(在线学习、continual learning)。训练用了 FP8 但精度对梯度极敏感,如果推理用 FP8 + 训练用 BF16,会出现"训练精度高但推理精度低"的不对称。建议推理与训练用同一精度。
场景 4:监管/合规要求 P99 精度损失 < 0.5%。医疗、金融、法律等场景的合规要求严格,FP8 的 P99 退化常常突破 2%,无法满足。坚持 BF16 + 必要的 FP32 accumulator。
场景 5:超长上下文(> 128k)。超长上下文的注意力模式与短文本不同,FP8 attention 在 > 32k 序列上 P99 退化比短序列高 3-5 倍,因为 attention score 的动态范围在长序列上更宽。坚持 BF16 attention + FP8 KV cache(折中方案)。
2026 年的精度调度地图会被两个新变量重塑:Blackwell B200/B300 的 FP4 Tensor Core 和 2027 年 Rubin 架构的 FP6/FP4 统一。B200 的 FP4 算力是 FP8 的 2 倍,但 FP4 的数值稳定性挑战比 FP8 更大——per-tensor scaling 在 4-bit 下完全不够,需要 group-wise scaling (每 32 个 element 一个 scale) + outlier-aware quantization (识别 outlier 并单独存储为 FP16)。我们预测 2026 年下半年到 2027 年初,会出现"FP4 weight + FP8 activation + BF16 attention"的混合精度范式,在 200B+ MoE 上把单卡吞吐推到 5000 tokens/s。
Rubin 架构(预计 2027 年发布)的关键变化是把 FP6 提升为一等公民精度——6-bit 是 FP4 与 FP8 的折中,动态范围比 FP4 宽 4 倍,精度损失比 FP8 低 30%。生产上可能形成"FP6 weight + FP8 KV cache + BF16 attention + FP32 logits"的四精度混合范式。
但硬件再变,调度原则不变:精度不是模型的属性,而是路径的决策。这条原则从 FP32/BF16 时代一直延续到 FP4/FP6 时代,是 LLM 推理系统工程化的核心心智模型。
如果你的团队刚开始做推理服务的精度调度,推荐按以下顺序推进:
第一阶段(1-2 周):基线建立。把所有推理路径的当前精度配置记录在配置中心,用一张表跟踪每个阶段的精度档位 + 数值代价 + 吞吐差异。不做任何改动,只为后续灰度对比建立基线。
第二阶段(2-4 周):单点优化。从最容易的优化点开始——decode weight 切 FP8(收益最大、风险最小)、prefill linear 切 FP8(收益次大、风险中)。每个改动必须配 P99 KL 散度的金标准 prompt 集验证,P99 退化 > 1% 立即回滚。
第三阶段(4-8 周):路径分离。把 prefill 和 decode 拆成两条独立路径(物理上可以是同一引擎的不同 kernel,逻辑上是不同的调度分支)。这一阶段引入 attention 切档位,需要 FP8 attention 内核(vLLM 0.7+ / SGLang 0.3+ 已支持)。
第四阶段(8-12 周):动态调度。引入精度档位控制器,根据 batch 大小、序列长度、显存空闲率动态切档位。这一阶段是工程复杂度最高的阶段,建议从小流量开始(5% → 20% → 50% → 100%),每阶段至少观察一周。
第五阶段(长期):联合优化。把精度档位放进调度器的目标函数,与模型版本管理、灰度发布、A/B 测试耦合起来,实现"模型更新 → 精度回归 → 自动灰度 → 自动回滚"的闭环。这一阶段需要团队具备完整的 MLOps + SRE 能力,通常是大厂专属。
精度调度是一个长跑型工程——短期内你看不到明显收益,但 6-12 个月后,当你的推理服务规模扩大 10 倍、模型版本迭代加速、长上下文场景占比上升时,精度调度的复利效应会显现:吞吐量提升 80-120%、单卡并发翻倍、显存利用率提高 40%、P99 精度退化稳定在 1% 以内。这四组数字是 2026 年生产推理服务的及格线。
最后给读者三条具体建议:第一,精度档位切换必须配金标准 prompt 集验证,不要相信"FP8 损失很小"的口号,自己的业务数据上测出来才是真;第二,可观测性先行,没有 per-token precision trace 之前不要碰动态调度,否则出了精度问题无法定位;第三,精度调度不是单点优化,是端到端联合优化,把精度档位、批大小、KV cache 策略、专家拓扑看作一个整体,不要把它们割裂开来调优。
一句话摘要:把 LLM 推理的精度选择从"选一个全局精度"升级为"为每个推理路径单独选精度,并把它们放进一个可观测、可调度、可回滚的端到端优化循环"——这是 2026 年生产推理服务的及格线。
Conversation
0 条