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

733 lines
16 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

你这个批评是对的。
上一个版本把问题讲成了“单请求 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