你这个批评是对的。 上一个版本把问题讲成了“单请求 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-limited,capacity 差异被隐藏,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。