Files
obsidian/projects/auto-tuner/Untitled 1.md

16 KiB
Raw Permalink Blame History

你这个批评是对的。 上一个版本把问题讲成了“单请求 service time + 泛泛 queueing”但对 vLLM 这类 continuous batching / bulk-service 系统来说,这样建模不够锋利。

先把问题修正:正确的服务对象不是 request而是 batch

对 TTFT 来说,真正被执行的是 prefill batch,不是单个请求。 因此更准确的建模方式是:

  • 先定义 batch runtime T_b(t)
  • 再定义 request 的 TTFT 是“排队等到自己所在 batch 被执行”加上“自己所在 batch 的执行时间”

而不是先写一个模糊的 $S_{\text{comp}} + S_{\text{comm}} + S_{\text{sched}}$。


一、最小而精确的建模框架

设:

  • 总 GPU 数固定为 G
  • TP 为 t
  • 则可部署 replica 数为

m_t = \frac{G}{t}

对某个 replica上面的第 b 个 prefill batch 记为 $\mathcal{B}_b$。

对 batch 中每个请求 $i$

  • prompt length 为 x_i
  • 该 batch 的总 prefill token 数为

Z_b = \sum_{i \in \mathcal{B}_b} x_i

二、batch runtime T_b(t) 应该怎么写

2.1 总式子

更准确地,应该写成:


T_b(t) = T_b^{\text{comp}}(t) + T_b^{\text{comm}}(t) + T_b^{\text{rt}}(t)

其中:

  • $T_b^{\text{comp}}(t)$:真正算子计算时间
  • $T_b^{\text{comm}}(t)$TP collective 通信时间
  • $T_b^{\text{rt}}(t)$runtime 固定开销,例如 launch gap、executor 调度、图切换、kernel 边界等

这里我故意不用模糊的 $S_{\text{sched}}$,而把它收敛为 runtime overhead residual。 因为对这类系统,“调度”大量体现为 batch 之间的时间缝隙和执行边界成本,而不是某个独立的物理项。


三、T_b^{\text{comp}}(t) 到底怎么算

3.1 prefill 的计算量不是只和 \sum x_i 成正比

对 decoder-only Transformer一个 prompt length 为 x 的请求,其 prefill 计算量可以抽象成:


F(x) = a x d^2 + b x^2 d

其中:

  • d 是 hidden size
  • a x d^2 对应 dense 部分QKV 投影、O 投影、MLP 等
  • b x^2 d 对应 self-attention 部分
  • a,b 吸收了层数 $L$、MLP expansion ratio、head 结构等常数

因此,一个 batch 的总 FLOPs 是:


F_b = \sum_{i \in \mathcal{B}*b} F(x_i)
= a d^2 \sum*{i \in \mathcal{B}*b} x_i + b d \sum*{i \in \mathcal{B}_b} x_i^2

如果把层数 L 显式写出来,则是:


F_b = L \left( a' d^2 \sum_{i \in \mathcal{B}*b} x_i + b' d \sum*{i \in \mathcal{B}_b} x_i^2 \right)

这比“S_{\text{comp}} 是计算项”要具体得多,因为这里直接告诉你:

prefill batch 的成本对长度分布是凸的,关键不是只有总 token 数 $Z_b$,而是还取决于 $\sum x_i^2$。


3.2 一个非常关键的结论length variance 直接进入 batch cost

因为:


\sum_{i \in \mathcal{B}_b} x_i^2
= n_b \left( \bar{x}_b^2 + \operatorname{Var}_b(x) \right)

其中:

  • n_b = |\mathcal{B}_b|
  • \bar{x}_b 是 batch 内平均 prompt length
  • \operatorname{Var}_b(x) 是 batch 内长度方差

所以在固定 n_b 和固定平均长度 \bar{x}_b 下:

  • 方差越大
  • \sum x_i^2 越大
  • prefill compute cost 越高

这就是一个很强的、可计算的 insight

heterogeneity 不只是“调度难”,而是直接提高了 attention 的物理计算量。

这也解释了为什么 coder window 往往更敏感: 它通常不是只有更高平均长度,而是更高的 \operatorname{Var}(x) 和更高的长尾。


3.3 把 FLOPs 映射成计算时间roofline 形式

给定 batch b 和 TP=$t$,其计算时间可以写成:


T_b^{\text{comp}}(t) = \max\{\frac{F_b}{\Pi_t}, \frac{Q_b}{B_t}\}

其中:

  • \Pi_t 是 TP=t 时该 instance 的有效算力
  • Q_b 是这个 batch 的总 memory traffic
  • B_t 是 TP=t 时该 instance 的有效 HBM 带宽

再进一步写:


\Pi_t = t \Pi_1 \eta_t^{\text{comp}}

B_t = t B_1 \eta_t^{\text{mem}}

其中:

  • \eta_t^{\text{comp}} \le 1 是 TP 扩大后由于 GEMM shape 变化、kernel utilization 降低带来的效率损失
  • \eta_t^{\text{mem}} \le 1 是内存系统扩展效率

于是:


T_b^{\text{comp}}(t)
=
\max
\{
\frac{F_b}{t \Pi_1 \eta_t^{\text{comp}}},
\frac{Q_b}{t B_1 \eta_t^{\text{mem}}}
\}

这已经比“S_{\text{comp}} 会随 TP 降低”精确很多了。 它明确说明:

  • 理想情况下compute/memory 时间会按 1/t
  • 但实际只能按 1/(t \eta_t)
  • \eta_t 通常随 t 变大而下降,所以是 次线性加速

四、T_b^{\text{comm}}(t) 到底怎么算

4.1 TP 的通信本质是 activation collective

对标准 tensor parallel每层通常会有若干个 activation 同步。 如果用 ring all-reduce 近似,大小为 n bytes 的一次 collective 时间是:


T_{\text{AR}}(n,t)
=
2 (t-1) \alpha
+
2 \frac{t-1}{t} \frac{n}{\beta}

其中:

  • \alpha 是每 hop latency
  • \beta 是链路带宽

如果每层平均有 k 次这样的 collective且每次 payload 与 batch activation 大小成正比:


n_b \approx q d Z_b

其中 q 是 dtype bytes那么


T_b^{\text{comm}}(t)
\approx
L k
\left(
2 (t-1)\alpha
+
2 \frac{t-1}{t} \frac{q d Z_b}{\beta}
\right)

这就是你要的更具体形式。


4.2 这个式子带来的核心含义

它说明 TP 通信开销有两个部分:

固定项


2 (t-1)\alpha

这是 latency-driven 的batch 小时特别痛。

线性 payload 项


2 \frac{t-1}{t} \frac{q d Z_b}{\beta}

这是 batch token 数越大越重。

所以:

  • 低负载、小 batch 时,通信固定项不一定主导,但也不会消失
  • 高负载、大 batch 时payload-driven 通信会上升
  • 更重要的是:即使 compute 时间下降communication 不会按 1/t 同步下降

这也是 TP 速度提升无法线性的根本原因。


五、把三项合起来:一个真正可用的 T_b(t)

综合起来,一个 batch 的执行时间可以写成:


T_b(t)
======

\max
\{
\frac{
L ( a' d^2 \sum_i x_i + b' d \sum_i x_i^2 )
}{
t \Pi_1 \eta_t^{\text{comp}}
},
\frac{Q_b}{t B_1 \eta_t^{\text{mem}}}
\}
+
L k
(
2 (t-1)\alpha
+
2 \frac{t-1}{t} \frac{q d Z_b}{\beta}
)
+
T_b^{\text{rt}}(t)

这已经是一个相当具体的 mechanistic model 了。


六、TTFT 应该怎么精确定义,而不是模糊说 W_q + E[S_t]

6.1 request 级别的精确定义

对请求 $i$,设它被分配到某个 replica并最终进入 prefill batch $b(i)$。 它的 TTFT 精确写成:


\mathrm{TTFT}_i(t)
==================

W_{q,i}(t) + T_{b(i)}(t)

其中:

  • W_{q,i}(t) 是它在自己 batch 真正开始执行前等待的时间
  • T_{b(i)}(t) 是它所在 batch 的执行时间

W_{q,i}(t) 又可以精确分解为:


W_{q,i}(t)
==========

R_i(t) + \sum_{u \in \mathcal{H}_i} T_u(t)

其中:

  • R_i(t) 是请求到达时当前正在跑的那个 batch 的 residual runtime
  • \mathcal{H}_i 是它前面还排着的 prefill batches 集合

这个式子是对的而且比“queueing delay”四个字更有结构。


6.2 为什么上次写的 E[S_t] 不够准确

因为对 continuous batching 系统,“request 的 service time”不是一个天然的一维随机变量

更自然的随机变量其实是 batch runtime $T_b(t)$。 request 的 TTFT 是:

  • 一个 residual batch
  • 加上若干个完整 batch
  • 再加上自己的 batch

所以更对的期望写法是围绕 $T_b(t)$,而不是围绕某个抽象的 $S_t$。


6.3 如果一定要写平均 queueing term应该怎么写

在稳态下,如果把 batch 看成 renewal process那么 residual life 的精确均值是:


\mathbb{E}[R_t]
===============

\frac{\mathbb{E}[T_b(t)^2]}{2 \mathbb{E}[T_b(t)]}

这比“queueing increases with variance”更具体因为它明确告诉你

batch runtime 的二阶矩直接进入等待时间。

如果再做一个独立性近似,则:


\mathbb{E}[W_q(t)]
\approx
\frac{\mathbb{E}[T_b(t)^2]}{2 \mathbb{E}[T_b(t)]}
+
\mathbb{E}[|\mathcal{H}|] , \mathbb{E}[T_b(t)]

其中 |\mathcal{H}| 是到达时前方排着的 batch 数。

这就已经比之前的 $E[S_t]$、W_q(t,w) 精确得多了。


七、真正关键的量不是 $E[S_t]$,而是 cluster capacity

如果你想分析“为什么高负载时小 TP 更好”,最关键的不是某个 request 的平均 service time而是

固定总 GPU 数下cluster 能提供的总服务能力到底是多少。


7.1 单 instance 的 batch token throughput

对 workload window $w$,定义 TP=t 时单 instance 的平均 prefill token throughput 为:


\mu_{t,w}
=========

\mathbb{E}
\left[
\frac{Z_b}{T_b(t)}
\middle| w
\right]

这里:

  • Z_b 是 batch token 数
  • T_b(t) 是 batch runtime

这是一个比 “$E[S_t]$” 更自然的核心量。


7.2 cluster 总 capacity

因为总 replica 数是 $m_t = G/t$,所以 cluster 级别的总 prefill capacity 是:


\Lambda_{t,w}
=============

\frac{G}{t} , \mu_{t,w}

这个式子非常关键。 它说明TP 的 tradeoff 不是抽象的,而是直接落在

  • 单 instance throughput\mu_{t,w}
  • replica 数:G/t

两者的乘积上。


八、为什么高负载下大 TP 很难赢:一个几乎是定理级别的结论

比较 TP=t 和 TP=1 的 cluster capacity


\frac{\Lambda_{t,w}}{\Lambda_{1,w}}
===================================

\frac{\mu_{t,w}}{t \mu_{1,w}}

因此,大 TP 只有在下面这个条件成立时,才能在固定总 GPU 数下提升 cluster capacity


\mu_{t,w} > t \mu_{1,w}

也就是说,单 instance throughput 必须超过线性扩展

但这是非常苛刻的,因为:

  • compute 只能理想到线性
  • 实际有 \eta_t^{\text{comp}} < 1
  • 还有额外的 T_b^{\text{comm}}(t)
  • 还有 runtime overhead

所以在稳态下,通常只能有:


\mu_{t,w} < t \mu_{1,w}

从而:


\Lambda_{t,w} \le \Lambda_{1,w}

这就是为什么在固定总 GPU 数下:

大 TP 几乎不可能提升饱和状态下的 cluster total capacity。

它能提升的是:

  • 单请求 latency
  • 单 instance 的 service time

但它通常不能提升总集群容量

这就是你图里最核心的 principle。


九、为什么低负载时 TP4 更好,但高负载时 TP1/TP2 更好

9.1 低负载:系统是 arrival-limited

观测到的 goodput 近似为:


g_{t,w}^{\text{obs}}
\approx
\frac{1}{G}
\min
\left{
\lambda_w^{\text{good}},
\Lambda_{t,w}
\right}

其中:

  • \lambda_w^{\text{good}} 是外部 offered good-token arrival rate
  • \Lambda_{t,w} 是系统 capacity

当低负载时:


\lambda_w^{\text{good}} \ll \Lambda_{t,w}

对所有 TP 都成立,所以:


g_{t,w}^{\text{obs}}
\approx
\frac{\lambda_w^{\text{good}}}{G}

因此不同 TP 的 observed goodput/GPU 看起来几乎一样。

这就解释了你在 time_scale=0.5 看到的现象:

  • goodput/GPU 差异很小
  • 但 TP4 latency 更好

因为这时真正显现出来的是:


T_b(4) < T_b(1)

而 capacity 差异被 arrival-limited 掩盖了。


9.2 高负载:系统开始 capacity-limited

当:


\lambda_w^{\text{good}} \approx \Lambda_{t,w}

或者超过它时,系统进入高利用率区。 此时:

  • observed goodput 开始接近 capacity
  • queueing term 开始爆炸

因为利用率可以写成:


\rho_{t,w}
==========

\frac{\lambda_w^{\text{good}}}{\Lambda_{t,w}}

而大 TP 通常让 \Lambda_{t,w} 下降,所以会让 \rho_{t,w} 上升。

一旦 \rho_{t,w} 接近 $1$,你就会看到:

  • queueing delay 急剧上升
  • p95 TTFT 急剧恶化
  • SLO pass 数下降
  • goodput/GPU 反而输给小 TP

这就解释了为什么高负载下 TP1/TP2 更优。


十、为什么 coder 比 chat 更容易出现这个现象

这可以从两个层面解释。

10.1 batch cost 的凸性更强地惩罚高方差长度分布

前面已经有:


F_b
===

a d^2 \sum_i x_i + b d \sum_i x_i^2

而:


\sum_i x_i^2
============

n_b \left( \bar{x}_b^2 + \operatorname{Var}_b(x) \right)

所以 coder 如果具有更高的:

  • long tail
  • length variance
  • long fraction

那么 batch runtime 就会更高,而且不是线性地更高。


10.2 queueing tail 还会额外放大 runtime variance

你真正关心的是 p95 TTFT。 而 p95 受的不仅是 $\mathbb{E}[T_b]$,还受 \mathbb{E}[T_b^2] 甚至更高阶尾部分布影响。

如果 coder 的 batch runtime 分布更 heavy-tail那么


\mathbb{E}[R_t]
===============

\frac{\mathbb{E}[T_b(t)^2]}{2 \mathbb{E}[T_b(t)]}

也会更大。

所以 coder 更早进入:

  • residual time 大
  • queue 深
  • p95 爆掉

的 regime。


十一、因此你应该怎么总结这条 principle

你现在最该写的不是:

负载越大TP 越小越好。

而应该写成下面这个更强、更准确的形式:


Principle

在固定总 GPU 数下TP 同时影响两个不同层面的量:

  1. 单 instance 的 batch runtime $T_b(t)$ 更大的 TP 往往降低 compute time因此改善低负载下的 TTFT。

  2. 集群总 capacity $\Lambda_{t,w} = \frac{G}{t}\mu_{t,w}$ 由于 TP 扩展通常只有次线性速度提升,且引入额外 collective 开销,因此 \mu_{t,w} 很难超过线性增长,导致 cluster capacity 通常随 TP 增大而不升反降。

因此:

  • 在低负载区,系统是 arrival-limitedcapacity 差异被隐藏TTFT 主要由 T_b(t) 决定,所以较大 TP 更优。
  • 在高负载区,系统是 capacity- / queueing-limited\Lambda_{t,w}\rho_{t,w} 主导表现,因此较小或中等 TP 更优。

十二、把你图里的 observation 严格落到这个模型上

你现在的图,其实支持的是下面这句话:

低载区

在 sampled low-load windows 上,


\lambda_w^{\text{good}} \ll \Lambda_{t,w}, \quad \forall t \in {1,2,4}

因此 observed goodput/GPU 相近;同时


T_b(4) < T_b(1)

所以 TP4 的 p95 TTFT 更好。


高载区

随着 time_scale 增大,\lambda_w^{\text{good}} 被放大,系统开始逼近:


\lambda_w^{\text{good}} \approx \Lambda_{t,w}

而由于通常有:


\Lambda_{4,w} < \Lambda_{2,w} \lesssim \Lambda_{1,w}

或者至少:


\Lambda_{4,w} < \Lambda_{2,w}

所以 TP4 更早进入高利用率区queueing tail 更早爆炸,于是:

  • goodput/GPU 开始输给小 TP
  • p95 TTFT 恶化更明显

这就是你观察到的 phase change。


十三、如果你要把这个模型真正用于预测,下一步该怎么做

你现在可以不再泛泛地说“workload affects TP”而是直接拟合下面这些量。

需要拟合的量

含义 如何估计
a,b prefill FLOP 模型常数 用 isolation microbench 拟合 batch runtime 对 \sum x_i\sum x_i^2 的系数
\eta_t^{\text{comp}} TP 计算扩展效率 对固定 batch 组合测 T_b^{\text{comp}} 反推
\alpha,\beta collective latency / bandwidth 参数 用 NCCL microbench 或 server log 中 collective 时间拟合
T_b^{\text{rt}}(t) runtime residual 用实测 T_b - T_b^{\text{comp}} - T_b^{\text{comm}} 得到
\mu_{t,w} 单 instance token throughput 直接从 trace replay 实测 batch token / runtime 统计
\Lambda_{t,w} cluster capacity \frac{G}{t}\mu_{t,w} 计算

十四、最本质的一句话

这条 principle 的本质其实可以压缩成一句话:

大 TP 优化的是“单个 batch 跑多快”,小 TP 优化的是“固定 GPU 预算下整个 cluster 同时能接多少活”。

在低负载时,前者主导。 在高负载时,后者主导。


如果你愿意,我下一条可以继续直接把这个模型再往前推一步,给你一版可写进论文的小节,包括:

  • 精确定义
  • Proposition
  • 证明思路
  • 为什么会出现 TP4 \rightarrow TP2 \rightarrow TP1 的 regime shift。