LLM 推理经济学:Batch、带宽与 KV Cache

来源:Dwarkesh Podcast 访谈 YouTube 视频 文字稿 嘉宾:Reiner Pope,MatX CEO;曾在 Google 从事 TPU 架构、编译器和软件效率工作。

读完这篇,你会知道这些问题背后的共同答案:

  • 为什么同一个模型可以有“快模式”和“慢模式”?
  • 为什么输出 token 通常比输入 token 贵?
  • 为什么长上下文常在 200K 附近突然涨价?
  • 为什么 batch 能把推理成本打下来,但不能无限便宜?
  • 为什么 MoE 模型天然适合塞进一个 GPU rack,而跨 rack 会变麻烦?
  • 为什么 pipeline parallelism 能省权重显存,却省不了 KV cache?
  • 为什么 prompt cache 命中便宜,写入却要按保留时间收费?
  • 为什么模型可能会被训练到远超 Chinchilla-optimal?

这些现象看起来像产品定价、模型架构、芯片互联、显存容量各管各的事,但底层其实反复回到同一个判断:一次 token 生成到底是被计算卡住,还是被显存带宽和 KV cache 卡住。

Roofline:一次 Decode 最少要多久

这组公式回答:生成下一个 token 的延迟下界是什么?

一次 decode 至少有两类工作:把激活权重乘一遍,以及从显存取出权重和 KV cache。二者可以部分重叠,但总时间不能低于更慢的那一个。

访谈里为了抓主项,忽略了 attention 里的小计算项,把 KV cache 的主要压力看成显存读取。

直觉:如果 \(B\) 很小,读权重的固定成本摊不开;如果 \(L\) 很长,读 KV cache 会越来越贵。
latency lower bound
符号定义
\(t_{\text{compute}}\)一次 decode batch 的矩阵乘计算时间。
\(t_{\text{weight}}\)从显存读取模型权重的时间。
\(t_{\text{KV}}\)从 KV cache 读取上下文历史的时间。
\(B\)decode batch 中同时生成下一个 token 的序列数。
\(L\)每条序列需要 attend 的历史上下文长度。
\(N_{\text{total}}, N_{\text{active}}\)总参数量,以及每个 token 实际激活并参与计算的参数量。
\(\mathrm{mem\_bw}\)可用于读取权重和 KV cache 的显存带宽。
\(\mathrm{bytes/token}\)一个历史 token 在 KV cache 中占用的字节数。
\[ t_{\text{compute}} = \frac{B\cdot N_{\text{active}}}{\mathrm{FLOPs}} \] \[ t_{\text{weight}} = \frac{N_{\text{total}}}{\mathrm{mem\_bw}} \] \[ t_{\text{KV}} = \frac{B\cdot L\cdot \mathrm{bytes/token}}{\mathrm{mem\_bw}} \] \[ t_{\text{memory}} = t_{\text{weight}} + t_{\text{KV}} = \frac{N_{\text{total}} + B\cdot L\cdot \mathrm{bytes/token}}{\mathrm{mem\_bw}} \] \[ t_{\text{decode}} \ge \max\!\left(t_{\text{compute}},\,t_{\text{memory}}\right) \]
Latency B t weight fetch KV fetch t_compute latency lower bound

Cost / token:为什么 Batch 便宜

这组公式回答:同一张 GPU 跑一次 batch,单 token 成本怎么变?

GPU 费用按时间算,但一次 batch 产出 \(B\) 个 token,所以单 token 成本近似是总时间除以 \(B\)。权重读取是固定成本,除以 \(B\) 后快速下降。

compute 和 KV fetch 都是每个序列各做一次,除以 \(B\) 后不会继续被摊薄。这解释了为什么“慢模式”不能无限便宜。

amortization
符号定义
\(\mathrm{cost/token}\)每生成一个 output token 分摊到的 GPU 时间成本。
\(t\)一次 batch forward pass 的总耗时。
\(B\)一次 batch 产出的 token 数;decode 时每条序列产出 1 个 token。
\(\mathrm{GPU\_cost/sec}\)GPU 每秒租用或折旧成本。
\(t_{\text{weight}}/B\)权重读取固定成本摊到每个 token 后的成本。
\(t_{\text{compute}}/B, t_{\text{KV}}/B\)计算和 KV 读取的 per-token 下界,无法靠继续增大 batch 无限摊薄。
\[ \mathrm{cost/token} \propto \frac{t}{B} \] \[ \mathrm{cost/token} = \mathrm{GPU\_cost/sec}\cdot\frac{t}{B} \] \[ \frac{t_{\text{weight}}}{B} = \frac{N_{\text{total}}}{\mathrm{mem\_bw}\cdot B} \] \[ \frac{t_{\text{compute}}}{B} = \frac{N_{\text{active}}}{\mathrm{FLOPs}} \] \[ \frac{t_{\text{KV}}}{B} = \frac{L\cdot \mathrm{bytes/token}}{\mathrm{mem\_bw}} \]
Cost / token B t / B compute / token KV / token memory / token = weight / B + KV actual cost / token = max(compute, memory) floor crossover batch

最小经济 Batch:什么时候 compute 追上 memory

这组公式回答:为了摊平权重读取,batch 大概要多大?

访谈中先忽略 KV cache,只比较“读一遍所有权重”和“对 batch 里所有 token 做矩阵乘”。令二者相等,就得到 balance batch size。

硬件的 \(\mathrm{FLOPs}/\mathrm{mem\_bw}\) 在许多 GPU 世代上大约稳定在 300 左右,因此 batch 主要由稀疏度决定。

batch balance
符号定义
\(B\)让权重读取和计算时间大致打平所需的 batch size。
\(\mathrm{FLOPs}/\mathrm{mem\_bw}\)硬件每读取 1 byte 显存数据可提供的计算量,也就是表里的拐点。
\(S\)稀疏度,定义为 \(N_{\text{total}}/N_{\text{active}}\)。
\(N_{\text{total}}\)需要从显存中保存并读取的总参数量。
\(N_{\text{active}}\)每个 token 实际参与矩阵乘的激活参数量。
\[ \frac{N_{\text{total}}}{\mathrm{mem\_bw}} = \frac{B\cdot N_{\text{active}}}{\mathrm{FLOPs}} \] \[ \frac{\mathrm{FLOPs}}{\mathrm{mem\_bw}} = B\cdot\frac{N_{\text{active}}}{N_{\text{total}}} \] \[ B = \frac{\mathrm{FLOPs}}{\mathrm{mem\_bw}} \cdot \frac{N_{\text{total}}}{N_{\text{active}}} \] \[ S=\frac{N_{\text{total}}}{N_{\text{active}}} \quad\Rightarrow\quad B\approx 300\cdot S \] \[ S_{\text{DeepSeek}} = \frac{256}{32} = 8 \quad\Rightarrow\quad B\approx 2400 \]

硬件拐点参考

下表里的“拐点”就是 \(\mathrm{FLOPs}/\mathrm{mem\_bw}\)。它给出硬件每读 1 byte 显存数据,理论上能做多少 BF16 FLOPs;这个值越高,越需要更大的 batch 才能把权重读取摊平。

加速器 年份 BF16 算力 HBM 容量 HBM 带宽 拐点
(FLOPs/byte)
Drain Time
(ms)
A100 (80GB)2020312 TFLOPS80 GB2.0 TB/s15640.0
H100 (SXM)2022989 TFLOPS80 GB3.35 TB/s29523.9
H200 (SXM)2024989 TFLOPS141 GB4.8 TB/s20629.4
B200 (SXM)20252250 TFLOPS192 GB8.0 TB/s28124.0
B300 / Blackwell Ultra2025 末2250 TFLOPS288 GB8.0 TB/s28136.0
Rubin R2002026 H2~5000 TFLOPS288 GB22 TB/s22713.1
Rubin Ultra2027 H2~10000 TFLOPS1024 GB32 TB/s31332.0
AMD MI300X20231307 TFLOPS192 GB5.3 TB/s24736.2
AMD MI325X20241307 TFLOPS256 GB6.0 TB/s21842.7
AMD MI355X20252300 TFLOPS288 GB8.0 TB/s28836.0
AMD MI450/MI455X2026 H210000 TFLOPS432 GB19.6 TB/s51022.0
TPU v5e2023197 TFLOPS16 GB0.82 TB/s24119.5
TPU v5p2023459 TFLOPS95 GB2.76 TB/s16634.4
TPU v6e (Trillium)2024918 TFLOPS32 GB1.64 TB/s56019.5
TPU v7 (Ironwood)20262307 TFLOPS192 GB7.4 TB/s31225.9
TPU v8t (Sunfish, 训练)2027 末~3150 TFLOPS216 GB6.53 TB/s48233.1
TPU v8i (Zebrafish, 推理)2027 末~2525 TFLOPS288 GB8.60 TB/s29433.5
AWS Trainium32025 末~1260 TFLOPS144 GB4.9 TB/s25729.4

吞吐与“火车发车”

这组公式回答:一个最小经济 batch 对应多少 tokens/s?

访谈把一次 batch 看成一班固定间隔发车的火车。每 \(\Delta t\) 秒发一班,每班产生 \(B\) 个 token,所以吞吐就是 \(B/\Delta t\)。

如果 \(\Delta t\) 是 15 到 20 ms,那么一秒约发 50 到 67 班,视频里用约 64 作为心算值。

departure schedule
符号定义
\(\Delta t\)两班 batch forward pass 之间的发车间隔。
\(B\)每班 batch 处理的序列数,也就是每班产出的 decode token 数。
\(\mathrm{tokens/sec}\)该推理配置的输出吞吐。
\[ \mathrm{tokens/sec} = \frac{B}{\Delta t} \] \[ \Delta t\approx 15\mathrm{ms}\sim20\mathrm{ms} \quad\Rightarrow\quad \mathrm{tokens/sec}\approx 64\cdot B \] \[ B\approx 2000 \quad\Rightarrow\quad \mathrm{tokens/sec} \approx 2000\cdot64 = 128000 \]

HBM Drain Time:为什么常见延迟是十几毫秒

这组公式回答:为什么一班 batch 的间隔常落在 15 到 20 ms?

如果一次 forward pass 的时间足以把 HBM 读一遍,显存带宽就被充分使用。再慢很多,相当于有时间把同一份 HBM 读多遍,通常没有意义。

memory drain time
符号定义
\(t_{\text{drain}}\)按满带宽把整块 HBM 读完一遍需要的时间。
\(\mathrm{memory\ capacity}\)单卡或单设备 HBM 容量。
\(\mathrm{memory\ bandwidth}\)单卡或单设备 HBM 读写带宽。
\[ t_{\text{drain}} = \frac{\mathrm{memory\ capacity}}{\mathrm{memory\ bandwidth}} \] \[ \frac{288\ \mathrm{GB}}{20\ \mathrm{TB/s}} \approx 15\ \mathrm{ms} \] \[ t_{\text{drain, HBM}} \approx 15\mathrm{ms}\sim20\mathrm{ms} \]

MoE:专家怎么铺到 GPU 上

这组公式回答:稀疏 MoE 的专家数、GPU 数、激活比例是什么关系?

MoE 的核心是总专家很多,但每个 token 只激活一小部分专家。专家并行就是把不同专家放在不同 GPU 上。

这个布局要求 GPU 之间做 all-to-all 通信;在一个 scale-up rack 内通信快,跨 rack 就会慢很多。

expert sharding
符号定义
\(\mathrm{experts/GPU}\)expert parallelism 下每张 GPU 承载的专家数量。
\(\mathrm{total\ experts}\)MoE 层中全部专家数量。
\(\mathrm{active\ experts}\)一个 token 路由时实际调用的专家数量。
\(\mathrm{active\ fraction}\)激活专家占总专家的比例,也就是专家层的稀疏激活比例。
\[ \mathrm{experts/GPU} = \frac{\mathrm{total\ experts}}{\mathrm{GPUs}} \] \[ \mathrm{experts/GPU} = \frac{256}{64} = 4 \] \[ \mathrm{active\ fraction} = \frac{\mathrm{active\ experts}}{\mathrm{total\ experts}} \] \[ \mathrm{active\ fraction} = \frac{32}{256} = \frac{1}{8} \]

Pipeline:什么时候跨 rack 可接受

这组公式回答:pipeline stage 之间的 scale-out 通信会不会成为瓶颈?

MoE 层内 all-to-all 会把一个 token 发给多个激活专家,通信量膨胀;pipeline 跨层换 rack 只发送一次激活。即使 scale-out 慢 8 倍,只要层内 all-to-all 工作足够多,跨 rack 就不是主瓶颈。

公式里的 2 来自 MoE all-to-all 的进出两次通信。

scale-up vs scale-out
符号定义
\(\mathrm{mem\_bw}_{\text{scale-up}}\)rack 内或 pod 内高速互联带宽,例如 NVLink / ICI。
\(\mathrm{mem\_bw}_{\text{scale-out}}\)跨 rack / 跨 pod 的网卡或 DCN 带宽。
\(t_{\text{scale-up}}\)MoE 层内 all-to-all 通信消耗的 scale-up 时间。
\(t_{\text{scale-out}}\)pipeline stage 换 rack 时跨 scale-out 网络发送激活的时间。
\(\mathrm{active\ experts}\)每个 token 在 MoE 层中发送到的专家数。
\(\mathrm{layers/stage}\)一个 pipeline stage 在同一 rack 内连续执行的层数。
\[ \mathrm{mem\_bw}_{\text{scale-up}} \approx 8\cdot\mathrm{mem\_bw}_{\text{scale-out}} \] \[ \frac{t_{\text{scale-up}}}{t_{\text{scale-out}}} \approx \frac{1}{8} \cdot 2 \cdot \mathrm{active\ experts} \cdot \mathrm{layers/stage} \] \[ \frac{t_{\text{scale-up}}}{t_{\text{scale-out}}} \ge 1 \] \[ 2\cdot\mathrm{active\ experts}\cdot\mathrm{layers/stage} \ge 8 \]

Scale-up / scale-out 参考

上面公式里的 8 是访谈中的粗略假设。实际 scale-up / scale-out 比值随代际变化很大;比值越低,跨 rack pipeline 越容易不成为瓶颈。TPU 行使用 Google Cloud 文档里的 ICI/DCN per-chip 口径;v8 行来自公开报道,scale-out 尚未见官方 per-chip 数字。

代际 Scale-up (NVLink / ICI) Scale-out (网卡 / DCN) 比值
H100 (2022)0.9 TB/s50 GB/s (400G IB)36x
B200 (2024)1.8 TB/s100 GB/s (800G CX8)18x
Rubin (2026)3.6 TB/s28.8 TB/s? 实际 ~200 GB/s per GPU2~18x¹
Rubin Ultra (2027)7.2 TB/s更高接近 1:1
TPU v5e (2023)0.4 TB/s ICI25 Gbps/chip DCN (~3.125 GB/s)128x
TPU v5p (2023)1.2 TB/s ICI50 Gbps/chip DCN (~6.25 GB/s)192x
TPU v6e / Trillium (2024)0.8 TB/s ICI100 Gbps/chip DCN (~12.5 GB/s)64x
TPU v7 / Ironwood (2026)1.2 TB/s ICI100 Gbps/chip DCN (~12.5 GB/s)96x
TPU v8t / Sunfish (2027 末)~2.4 TB/s ICI²未公开未公开
TPU v8i / Zebrafish (2027 末)~2.4 TB/s ICI²未公开未公开

² 公开报道给出 TPU v8 ICI 为 19.2 Tb/s per chip,这里按 8 bit/byte 换算为约 2.4 TB/s。

显存容量:Pipeline 为什么省权重但不省 KV

这组公式回答:pipeline parallelism 对每张 GPU 的显存占用有什么影响?

权重可以同时被 expert parallelism 和 pipeline parallelism 分摊,所以 \(P\) 越大,每张 GPU 存的权重越少。

KV cache 看起来也能按 layer 分摊,但为了让 pipeline 不空转,必须同时放进 \(P\) 个 micro-batch。一个 \(P\) 抵消另一个 \(P\),所以每张 GPU 的 KV 项不随 pipeline stages 下降。

memory capacity per GPU
符号定义
\(C_{\text{total}}\)模型权重和 KV cache 加起来的总显存需求。
\(c_{\text{mem}}\)分摊到每张 GPU 上的显存需求。
\(E\)expert parallelism extent,同一层专家横向切到多少个 GPU。
\(P\)pipeline stages,模型层纵向切到多少个 stage / rack。
\(b\)单个 micro-batch 的序列数。
\(B\)全局 batch;为了填满 \(P\) 个 stage,稳态近似 \(B=P\cdot b\)。
\(L\cdot\mathrm{bytes/token}\)单条序列的 KV cache 显存占用。
\[ C_{\text{total}} = N_{\text{total}} + B\cdot L\cdot \mathrm{bytes/token} \] \[ c_{\text{mem}} = \frac{ N_{\text{total}} + B\cdot L\cdot \mathrm{bytes/token} }{E\cdot P} \] \[ B = \mathrm{num\ microbatches}\cdot b \] \[ \mathrm{num\ microbatches}=P \quad\Rightarrow\quad B=P\cdot b \]

为什么 \(B=P\cdot b\):如果有 \(P\) 个 pipeline stage,只放 1 个 micro-batch 时,大部分 stage 都在等前一个 stage 传来激活,形成 bubble。为了让每个 stage 同时有活干,稳态时需要 \(P\) 个 micro-batch 同时在途;每个 micro-batch 大小是 \(b\),所以全局 batch 是 \(P\cdot b\)。

\[ c_{\text{mem}} = \frac{N_{\text{total}}}{E\cdot P} + \frac{P\cdot b\cdot L\cdot \mathrm{bytes/token}}{E\cdot P} \] \[ c_{\text{mem}} = \frac{N_{\text{total}}}{E\cdot P} + \frac{b\cdot L\cdot \mathrm{bytes/token}}{E} \]
time rack 0 1 2 3 bubble (fill) bubble (drain) F0F1F2F3 F0F1F2F3 F0F1F2F3 F0F1F2F3 B3B2B1B0 B3B2B1B0 B3B2B1B0 B3B2B1B0 Forward pass Backward pass Bubble (idle time)

训练 / RL / 推理:为什么可能远超 Chinchilla

这组公式回答:如果最终目标是总成本最低,预训练 token 数该和什么量级相当?

Chinchilla 只优化训练 compute。但产品部署后,模型还会消耗大量推理 compute;RL 也会用 decode 生成轨迹。访谈用启发式:最优点附近,各项成本往往大致均衡。

2022 年 DeepMind 的 Chinchilla 论文结论是:在固定训练算力预算下,模型参数量 \(N\) 和训练 token 数 \(D\) 应该近似等比例扩张。常用经验写法是 \(D_{\text{Chinchilla}}\approx20\cdot N\),也就是每 1 个参数大约配 20 个训练 token。

因此,预训练数据量、RL 数据量、模型生命周期内的推理 token 量会落在相近数量级。若推理规模巨大,就会推动模型比 Chinchilla 推荐值“过训练”很多。

compute budget balance
符号 定义
\(C_{\text{total}}\)总 compute 成本,粗略按 FLOPs 或 GPU 时间计。
\(C_{\text{pretrain}}\)预训练阶段的 compute 成本。
\(C_{\text{RL}}\)RL / post-training 阶段的 compute 成本,包括 rollout 生成和训练更新。
\(C_{\text{inference}}\)模型上线后服务用户请求的推理 compute 成本。
\(N_{\text{active}}\)每个 token 实际参与前向计算的参数量;dense model 里近似等于模型参数量,MoE 里是激活参数量。
\(N\)Chinchilla 原论文中的 dense model 参数量。
\(D_{\text{pretrain}}\)预训练 token 数。
\(D_{\text{RL}}\)RL 阶段处理的 token 数,包含用于生成/训练的轨迹 token。
\(D_{\text{inference}}\)模型生命周期内实际服务出来的推理 token 数。
\(\gamma\)RL 每 token 的有效 compute 系数;forward-only 约 2,forward+backward 上限约 6,中间还受采样、丢弃轨迹和低 MFU 影响。
\(D_{\text{Chinchilla}}\)Chinchilla scaling law 建议的训练 token 数。
\[ C_{\text{total}} = C_{\text{pretrain}} + C_{\text{RL}} + C_{\text{inference}} \] \[ C_{\text{pretrain}} \approx 6\cdot N_{\text{active}}\cdot D_{\text{pretrain}} \] \[ C_{\text{RL}} \approx \gamma\cdot N_{\text{active}}\cdot D_{\text{RL}}, \qquad \gamma\approx2\sim6 \] \[ C_{\text{inference}} \approx 2\cdot N_{\text{active}}\cdot D_{\text{inference}} \]

系数 6 来自 transformer 训练常用的 \(6ND\) 估算:一次 forward 约 \(2ND\),backward 和梯度相关计算再带来额外成本。纯 inference 只需要 forward,所以写成 \(2ND\)。

\[ C_{\text{pretrain}} \approx C_{\text{RL}} \approx C_{\text{inference}} \] \[ D_{\text{inference}} = \mathrm{tokens/sec}\cdot \mathrm{deployment\ time} \] \[ 50\mathrm{M/s}\cdot2\mathrm{\ months} \approx 2\cdot10^{14}\ \mathrm{tokens} \] \[ D_{\text{Chinchilla}} \approx 20\cdot N \] \[ 7\mathrm{B}\ \text{params} \Rightarrow D_{\text{Chinchilla}} \approx 140\mathrm{B}\ \text{tokens} \] \[ 70\mathrm{B}\ \text{params} \Rightarrow D_{\text{Chinchilla}} \approx 1.4\mathrm{T}\ \text{tokens} \]

这里 \(N\) 是 Chinchilla 原论文里的 dense model 参数量。访谈后面的心算把 frontier MoE 的 active/effective 参数量代进去,是为了做量级估算。

\[ \mathrm{overtraining\ factor} = \frac{D_{\text{pretrain}}}{D_{\text{Chinchilla}}} \approx \frac{200\mathrm{T}}{2\mathrm{T}} = 100 \]

长上下文定价:为什么常在 200K 后涨价

这组公式回答:API 在长上下文处涨价,可能透露了什么硬件/模型信息?

如果价格在某个 context length 附近跳档,通常说明这个区间开始显著增加 memory-bound 风险:上下文越长,decode 每一步越可能被 KV cache 读取拖慢。

这里的 \(\mathrm{bytes/token}\) 对应原文里的 “bytes for one token”:不是输入文本 token 本身的大小,而是模型为了让未来 token attend 到这个历史 token,需要在 KV cache 中保存的 key/value 向量大小。

200K 不是物理常数,而是一个产品上好用、工程上合理的阈值:它大致落在现代前沿模型从“主要被计算量限制”转向“更容易被 KV 读取限制”的区间。实际阈值会被模型结构、KV 压缩、稀疏 attention、硬件带宽和产品定价策略共同移动。

背景信息:公开 API 是否按 context length 分档

公司 是否按 context length 分档 关键阈值 怎么收费
OpenAI 你提供的资料显示部分旗舰模型分 short / long context;我在当前可访问的 OpenAI pricing 页未核到 GPT-5.5 / 272K 这条。 约 272K input tokens(待具体 GPT-5.5 页面核验) 按你给的口径:超过阈值后,long context input 约 2x、output 约 1.5x,并对整次 session/request 应用更高费率。当前可访问的 OpenAI pricing 页面列出了模型 token 价格,但未显示该 GPT-5.5 long-context 档。
Anthropic / Claude 是。当前官方文档写明 1M context 请求超过 200K tokens 会自动收 premium long-context rates。 200K tokens 官方 context-window 文档写明:超过 200K tokens 的请求按 2x input、1.5x output 的 premium rates 收费;prompt caching 仍按 5m write 1.25x、1h write 2x、cache read 0.1x 叠加。参考 context windows 和 pricing。
Google Gemini 是。Pro / Priority 等模型明显按 prompt length 分档;Flash / Flash-Lite 等便宜模型不一定分档。 200K tokens Gemini pricing 页按 prompts <= 200K 和 > 200K 分档。例如 Gemini 2.5 Pro 标准价 input $1.25 / MTok <=200K、$2.50 / MTok >200K;output $10 / MTok <=200K、$15 / MTok >200K;context caching 也从 $0.125 到 $0.25。参考 Gemini pricing。
long context crossover
符号定义
\(L\)长上下文长度;公开 API 常在 200K 左右开始设置 long-context 档位。
\(\mathrm{bytes/token}\)一个历史 token 的 KV cache 字节数。
\(N_{\text{active}}\)每个 token 的激活参数量,决定 decode 计算成本。
\(\mathrm{FLOPs}/\mathrm{mem\_bw}\)硬件计算带宽与显存带宽之比。
\(n_{\text{KV heads}}, d_{\text{head}}\)KV head 数和每个 head 的维度,决定 KV cache 的向量大小。
\[ \frac{B\cdot L\cdot \mathrm{bytes/token}}{\mathrm{mem\_bw}} = \frac{B\cdot N_{\text{active}}}{\mathrm{FLOPs}} \] \[ \frac{L\cdot \mathrm{bytes/token}}{\mathrm{mem\_bw}} = \frac{N_{\text{active}}}{\mathrm{FLOPs}} \] \[ \mathrm{bytes/token} = \frac{N_{\text{active}}\cdot\mathrm{mem\_bw}} {\mathrm{FLOPs}\cdot L} \] \[ \mathrm{bytes/token} = \frac{N_{\text{active}}} {(\mathrm{FLOPs}/\mathrm{mem\_bw})\cdot L} \]

为什么 200K 经常出现:把 \(N_{\text{active}}\) 看成几十 B 到百 B 量级,把 \(\mathrm{FLOPs}/\mathrm{mem\_bw}\) 看成 200~500 FLOPs/byte,把模型工程后的有效 KV 读取看成 KB/token 量级,公式自然会给出十万到数十万 token 的 crossover 区间。

\[ L^\* \sim \frac{50\mathrm{B}\sim150\mathrm{B}} {(200\sim500)\cdot(1\sim3)\mathrm{KB}} \approx 100\mathrm{K}\sim500\mathrm{K} \]

因此 200K 更像是这个宽区间里的定价锚点:低于它,多数请求仍能被较好地批处理和摊销;高于它,KV cache 容量、带宽、调度碎片和尾延迟都会更容易恶化,所以 provider 倾向于提高整段请求的价格。

\[ \mathrm{bytes/token} \approx n_{\text{attn layers}}\cdot2\cdot d_{\text{head}}\cdot n_{\text{KV heads}}\cdot\mathrm{bytes/element} \]
图中符号定义
\(L\) / \(\mathrm{len\_ctx}\)当前请求的上下文长度,也就是需要读取多少历史 token 的 KV cache。
\(t_{\text{compute}}(L)\)每 token 的计算时间。简化模型里近似不随 \(L\) 增长,所以图中是水平线。
\(t_{\text{mem}}(L)\)每 token 的显存读取时间,主要来自 KV cache;随 \(L\) 近似线性增长。
\(L^\*\)crossover context length,满足 \(t_{\text{compute}}(L^\*)=t_{\text{mem}}(L^\*)\)。200K 是常见产品阈值,不必等同于某个模型的精确物理拐点。
\(t_{\text{cost}}(L)\)真实成本下界,取 compute 和 memory 两条曲线中更慢的一条。
\[ t_{\text{compute}}(L) \approx \frac{B\cdot N_{\text{active}}}{\mathrm{FLOPs}} \quad \text{近似为常数} \] \[ t_{\text{mem}}(L) \approx \frac{N_{\text{total}} + B\cdot L\cdot \mathrm{bytes/token}}{\mathrm{mem\_bw}} \] \[ t_{\text{cost}}(L) \ge \max\!\left(t_{\text{compute}}(L),\,t_{\text{mem}}(L)\right) \]

图形解释

图里的横轴是上下文长度 \(L\)。\(t_{\text{compute}}\) 近似不随 \(L\) 增长,所以画成水平线;\(t_{\text{mem}}\) 随 KV cache 读取长度近似线性上升。实际成本下界取两者的最大值,因此在 crossover 之前近似 compute-bound,之后转为 memory-bound。API 的 tier pricing 通常会把连续成本曲线离散成几档,确保长上下文区间仍覆盖成本。

Cost len_ctx t t_compute t_mem pricing threshold (~200K) tier 1 price tier 2 price

Prefill vs Decode:为什么输出 token 更贵

这组公式回答:为什么输入 token 常比输出 token 便宜?

decode 的 \(L_{\text{pass}}=1\),每次只生成一个 token,所以 KV 和权重读取无法在很多 token 上摊薄。prefill 一次处理很多输入 token,memory 成本可以除以 \(L_{\text{pass}}\)。

compute per token 大致不变,但 memory per token 随 pass 长度下降。因此如果 decode 价格显著更高,说明 decode 端很可能强烈受 memory bandwidth 限制。

prefill amortizes memory
符号定义
\(L_{\text{pass}}\)一次 forward pass 同时处理的 token 数;decode 为 1,prefill 大于 1。
\(t_{\text{compute, pass}}\)整个 pass 的计算时间。
\(t_{\text{compute/token}}\)按 pass 内 token 数摊销后的每 token 计算时间。
\(t_{\text{memory/token}}\)按 pass 内 token 数摊销后的每 token 显存读取时间。
\(B\)同一 pass 并行处理的序列数。
\[ L_{\text{pass}}=1 \quad \text{for decode} \] \[ L_{\text{pass}}>1 \quad \text{for prefill} \] \[ t_{\text{compute, pass}} \propto \frac{B\cdot L_{\text{pass}}\cdot N_{\text{active}}}{\mathrm{FLOPs}} \] \[ t_{\text{compute/token}} = \frac{t_{\text{compute, pass}}}{L_{\text{pass}}} \propto \frac{B\cdot N_{\text{active}}}{\mathrm{FLOPs}} \] \[ t_{\text{memory/token}} = \frac{t_{\text{memory}}}{L_{\text{pass}}} \propto \frac{1}{L_{\text{pass}}} \]
Cost per token vs pass length L_pass time / token compute / token ≈ flat memory / token ∝ 1 / L_pass actual cost ≈ max(compute, memory) decode L_pass = 1 prefill L_pass ≫ 1 decode memory premium

Prompt Cache:存 KV 还是重算 KV

这组公式回答:cache hit 为什么便宜,cache write 为什么按保留时间收费?

没有缓存:模型只看到 token id,得从 embedding 开始把整段前缀重新跑一遍 prefill,把每层 KV 重新算出来。多轮对话里前缀越长,浪费越夸张。

缓存命中:KV 已经躺在某个存储层级,只需把字节搬回 HBM 拼上去,省掉整段 prefill 的算力。搬运永远比重算便宜,这是 prompt cache 经济上成立的根本原因。

存储层级三个维度耦合:越快的层级越贵、越小;越便宜的层级越慢、越大。没有既快又便宜又大的选项。

保留时长决定放哪一层:

  • 秒级复用:HBM,贵但占用短。
  • 分钟级:DDR,单位成本低得多。
  • 小时到天级:Flash / disk,慢但极便宜。

热数据待顶层,冷数据沉底层,和 CPU cache、CDN 是同一套分层思路。

storage tiers
符号定义
\(\mathrm{cost}_{\text{remat/token}}\)缓存失效后,为一个 token 重新计算 KV cache 的成本。
\(\mathrm{hold\ cost}\)把 KV cache 保存在某个 memory tier 中每秒占用的机会成本。
\(\mathrm{HBM}, \mathrm{DDR}\)不同存储层级;HBM 快但贵,DDR/flash/disk 慢但更便宜。
\(t_{\text{retrieve, DDR}}\)把 DDR 中的 KV cache 取回可用层级所需时间。
\(t_{\text{drain}}\)按带宽读完整个存储层容量所需时间,用来判断适合的保留时长。
\[ \mathrm{cost}_{\text{remat/token}} \approx \frac{N_{\text{active}}}{\mathrm{FLOPs}} \cdot \mathrm{GPU\_cost/sec} \] \[ \mathrm{hold\ cost}_{\text{HBM/token/sec}} = \frac{\mathrm{bytes/token}}{\mathrm{HBM\ capacity}} \cdot \mathrm{GPU\_cost/sec} \] \[ \mathrm{hold\ cost}_{\text{DDR/token/sec}} = \frac{\mathrm{bytes/token}}{\mathrm{DDR\ capacity}} \cdot \mathrm{DDR\_cost/sec} \] \[ t_{\text{retrieve, DDR}} = \frac{\mathrm{bytes/token}}{\mathrm{DDR\ bandwidth}} \] \[ t_{\text{drain}} = \frac{\mathrm{storage\ capacity}}{\mathrm{storage\ bandwidth}} \]

存储层级 drain time 量级

层级 访谈里的 drain time 量级
HBM\(20\mathrm{ms}\)
DDR\(1\sim10\mathrm{s}\)
Flash\(\sim1\mathrm{min}\)
Spinning disk\(\sim1\mathrm{hour}\)

Feistel / RevNet:用可逆结构省训练显存

这组公式回答:密码学里的 Feistel 结构如何变成可逆神经网络?

Feistel construction 的关键是:即使 \(f\) 本身不可逆,整个二元映射仍可逆,因为输出中保留了 \(x\)。RevNet 用类似结构让前向激活可以在反向传播时重算,从而少存 activation。

这是“多花 compute,少用 memory”的例子;与 KV cache 的“多用 memory,少花 compute”正好相反。

reversible block
符号定义
\(x,y\)可逆 block 的两个输入分量;保留其中一半让整体映射可逆。
\(f(x)\)任意不一定可逆的子网络,例如 MLP 或 transformer layer。
\(z\)临时记号,表示 \(y+f(x)\)。
\(x_{\text{out}}, y_{\text{out}}\)可逆 block 的两个输出分量。
\[ (x,y) \mapsto (x,\ y+f(x)) \] \[ z=y+f(x) \quad\Rightarrow\quad y=z-f(x) \] \[ x_{\text{out}} = y+f(x) \] \[ y_{\text{out}} = x \] \[ x = y_{\text{out}} \] \[ y = x_{\text{out}}-f(y_{\text{out}}) \]