SlidingServe:面向LLM推理的SLO感知滑动窗口调度

笔记日期: 2026-06-07 笔记作者: Zhongzhu Zhou 论文标题: Beyond Greedy Chunking: SLO-Aware Sliding-Window Scheduling for LLM Inference 作者: Yuansheng Chen, Yue Zhang, Xuan Mo, Weigang Wu, Jialun Li arXiv: 2606.05933v1,2026-06-05 状态: arXiv 预印本,2026

一句话总结

SlidingServe 是一个 SLO 感知的 LLM 在线推理调度系统,核心创新在于:用滑动窗口将贪婪分块扩展到跨迭代联合优化(SlidingChunker),用动态规划实现批次内细粒度请求选择(BatchConstructor),配合轻量级批次延迟预测器和多级优先级排序,在高负载下最高提升 30% 服务容量,SLO 违约率降低 16%–53%。其本质是把 LLM 调度中两个长期被忽视的问题——跨迭代预算分配与批次内请求相互影响——转化为形式化的优化问题并高效求解。

1. 前置知识

1.1 LLM 推理的生命周期:预填充、解码、TTFT 与 TBT

大语言模型(LLM)的推理是自回归的:每一次前向传播只生成一个新 token。一个服务请求的完整生命周期由两个截然不同的阶段组成:

预填充(Prefill)阶段: 对用户输入(prompt)中的所有 token 进行一次或几次前向计算,同时建立 KV 缓存(Key-Value Cache)。预填充是**计算密集型(compute-bound)**的:注意力计算的复杂度约为 O(PS)O(P \cdot S),其中 PP 是 prompt 长度,SS 是已处理的序列长度。对于长 prompt(数千 token),这一阶段可能占用 GPU 数百毫秒。

解码(Decode)阶段: 预填充完成后,模型逐 token 生成输出。每一步解码处理一个新 token,同时注意力操作需要访问完整的 KV 缓存。解码是**内存带宽密集型(memory-bandwidth-bound)**的,每步通常耗时几毫秒。

用户感知到的两个核心延迟指标:

  • TTFT(Time to First Token,首 token 延迟): 从请求到达到第一个输出 token 生成的时间。它反映用户等待响应开始的感受,对交互式场景至关重要。
  • TBT(Time Between Tokens,也叫 ITL,token 间延迟): 连续两个输出 token 之间的时间间隔。TBT 决定流式生成的流畅感——如果 TBT 超过约 100 ms,用户会感受到明显的停顿。

SlidingServe 将两者统一建模为逐 token 的截止时间序列。设请求 ii 在时刻 aia_i 到达,TTFT SLO 为 LittftL_i^{\text{ttft}},TBT SLO 为 LitbtL_i^{\text{tbt}},已生成 kk 个 token,则第 (k+1)(k+1) 个 token 的截止时间为:

di,k+1=ai+Littft+kLitbt(1)d_{i,k+1} = a_i + L_i^{\text{ttft}} + k \cdot L_i^{\text{tbt}} \tag{1}

这个统一公式让调度器在整个请求生命周期内都能用同一套约束框架来衡量 SLO 是否满足。

1.2 KV 缓存的内存动态与 PagedAttention

每个处于解码阶段的请求都需要在 GPU 显存中保留它的 KV 缓存——存储所有已处理 token 的 Key 和 Value 张量。对于一个有 LL 层、隐层维度为 dd 的模型,序列长度为 SS 时,KV 缓存大小约为:

KV cache2LdSsizeof(dtype) bytes(2)\text{KV cache} \approx 2 \cdot L \cdot d \cdot S \cdot \text{sizeof(dtype)} \text{ bytes} \tag{2}

以 7B 参数模型(32层,隐层 4096,BF16)为例,每个 token 大约需要 512 KB 的 KV 缓存。对于 1024 token 的序列,单请求 KV 缓存超过 512 MB。

传统静态内存分配方案的问题:为每个请求预留一块连续内存,导致严重的内存碎片化(内部碎片率高达 30–40%)。PagedAttention(vLLM, Kwon et al. 2023)借鉴操作系统的虚拟内存机制,将 KV 缓存分割成固定大小的”页(page)“,按需分配,彻底消除内存碎片,使得更多请求可以同时共存于一个批次,大幅提升 GPU 利用率。

1.3 分块预填充(Chunked Prefill)与 SLO 的关系

传统服务方案中,长 prompt 的预填充作为一个整体执行,可能持续 200–500 ms。在此期间,批次中所有解码请求都无法生成新 token,TBT 剧烈波动,用户体验严重受损。

分块预填充(Sarathi-Serve, Agrawal et al. 2023)将长 prompt 的预填充拆分为若干大小为 CC 的”块(chunk)“,与解码请求在同一个迭代循环中交替执行。这样:

  1. 解码请求的 TBT 近似上界变为 Tchunk+Tdecode-onlyT_{\text{chunk}} + T_{\text{decode-only}},而非 Tfull prefillT_{\text{full prefill}}
  2. 调度器获得了一个新的可调旋钮:块大小 CC,它是吞吐量与延迟之间权衡的核心参数。

块大小的选择并不简单:CC 太大会延长当前迭代时间,损害 TBT;CC 太小会导致 GPU 利用率不足,恶化等待请求的 TTFT。SlidingServe 的核心贡献之一就是提供了系统性地确定最优 CC 的方法。

1.4 连续批处理的调度循环

连续批处理(Continuous Batching)(Orca, Yu et al. 2022)打破了传统静态批处理”等待整个批次完成”的模式,允许系统在每次迭代边界动态增减请求。其核心调度循环如下:

循环:
    1. 从运行队列中选取当前批次(所有正在解码的请求)
    2. 从等待队列中选取可加入的新请求(受 KV 缓存空间限制)
    3. 执行一次前向传播(所有批次请求推进一步)
    4. 移除已完成生成的请求;更新剩余请求状态
    5. 回到步骤 1

连续批处理消除了”短请求等待长请求完成”的队头阻塞,显著提升了 GPU 利用率。但它没有解决预填充的计算密集性问题——在 GPU 专注于长 prompt 预填充时,同批次的解码请求仍然无法生成新 token。

分块预填充引入的新复杂性: 将预填充分块后,调度器需要在每次迭代中同时决定:①哪些解码请求继续解码?②哪些预填充请求的下一个 chunk 被调度?③各 chunk 的大小是多少?这三个决策互相耦合,形成了 SlidingServe 所面对的核心调度问题。

1.5 SLO、Goodput 与服务质量指标

服务级别目标(SLO,Service Level Objective): 系统承诺为每个请求满足的延迟上界,通常包括 TTFT 和 TBT 两个维度。

Goodput(有效吞吐量): 在满足 SLO 的前提下每秒处理的请求数。SlidingServe 将 goodput 定义为在 SLO 违约率不超过 1% 条件下的最高请求率。Goodput = 吞吐量 × SLO 达标率,是比纯吞吐量更能反映服务质量的指标。

连续批处理(Continuous Batching)(Orca, Yu et al. 2022):允许在每次迭代边界动态地将新请求加入批次、将完成的请求移出,避免了静态批处理中短请求等待长请求的队头阻塞问题。这是现代高吞吐 LLM 服务的基础架构。

系统延迟预算约束。 对于每轮迭代,调度器需要满足:

TiterminiDsafesi(t)(2)T_{\text{iter}} \leq \min_{i \in \mathcal{D}_{\text{safe}}} s_i(t) \tag{2}

其中 si(t)=di,kts_i(t) = d_{i,k} - t 是解码请求 ii 在当前调度时刻 tt 的 TBT 余量,Dsafe\mathcal{D}_{\text{safe}} 是当前窗口内 TBT 截止时间有效的解码请求集合。这个约束是 SlidingChunker 确定最大允许迭代时间的基础。

Goodput 定义。 SlidingServe 采用的核心评估指标:

Goodput=request rate×SLO attainment(3)\text{Goodput} = \text{request rate} \times \text{SLO attainment} \tag{3}

具体操作化为:在 SLO 违约率 ≤ 1% 的条件下系统能处理的最高请求率(req/s)。纯吞吐量最大化会以牺牲 SLO 为代价;Goodput 是两者的综合度量。

2. 问题背景与动机

2.1 贪婪分块调度的失效模式

分块预填充场景下最常见的调度策略被论文称为”单步调度(Single-step Scheduling)“:每轮迭代时,根据最紧迫的解码请求的 TBT 余量计算当前轮的最大允许执行时间,然后选择不超过这个时间约束的最大块大小。

Cgreedy=TimeToBudget ⁣(miniDsi(t))(3)C_{\text{greedy}} = \text{TimeToBudget}\!\left(\min_{i \in \mathcal{D}} s_i(t)\right) \tag{3}

这个策略看似合理,实则存在两个根本性的缺陷:

缺陷一:跨迭代的时间槽浪费。 GPU 执行时间与块大小之间的关系并非线性——注意力计算是二次的(prefill 阶段),内存带宽是主瓶颈(decode 阶段),两者结合产生了凸形的时间-批次大小曲线。当前轮贪婪地用满所有余量,会导致下一轮的可用余量极少,可能退化为纯解码批次,整体吞吐量反而下降。如果在当前轮适当”留一些余量”给下一轮,两轮合计可以处理更多 token。

缺陷二:批次内 TTFT 盲区。 批次构建后,批次中的所有请求需要等待整个批次执行完毕才能推进状态。即使调度器已经把请求加入了批次,如果批次预测执行时间超过了该请求的 TTFT 余量,该请求仍然会违约。当前系统完全忽视了这种”批次内请求间的相互干扰”。

graph TD
    A[请求到达\nTTFT 截止时间 = 100ms] --> B{批次构建策略}
    B -->|贪婪填充| C[批次含4个预填充请求\n预测执行时间 = 120ms]
    C --> D[TTFT 违约!\nr1、r2、r3 均超时]
    B -->|SlidingServe| E[BatchConstructor\n移除 r2、r4]
    E --> F[批次仅含 r1、r3\n预测执行时间 = 80ms]
    F --> G[r1、r3 按时获得首 token\nr2、r4 推迟到下轮]
    G --> H[r2、r4 在下一迭代\n在其余量内完成预填充]

图1:批次内 TTFT 盲区问题。贪婪策略填满批次导致 3 个请求违约;BatchConstructor 精确剔除低优先级请求,确保关键请求按时完成。

2.1b 跨迭代依赖忽视的代价:量化示例

用一个具体例子量化贪婪策略的代价。设服务器运行 Qwen2.5-7B (TP2),解码请求 TBT SLO = 50 ms,当前有 8 个解码请求,最小余量 sminD=40s_{\min}^D = 40 ms:

  • 贪婪策略: Cgreedy=TimeToBudget(40)=1800C_{\text{greedy}} = \text{TimeToBudget}(40) = 1800 token,批次执行 38 ms。

    • 下一轮余量仅剩 5038=1250 - 38 = 12 ms → Cnext=TimeToBudget(12)=350C_{\text{next}} = \text{TimeToBudget}(12) = 350 token。
    • 两轮合计:1800+350=21501800 + 350 = 2150 token。
  • SlidingChunker: 三分搜索找到 B=1100B^* = 1100,下一轮分配 BΣ1100=800B_\Sigma - 1100 = 800 token。

    • 两轮合计:1100+800=19001100 + 800 = 1900 token。

等等,贪婪反而更多? —— 关键在于”下一轮的 350 token”可能因为批次太小而 GPU 严重利用不足(decode-heavy 模式),实际吞吐效率更低。而且,贪婪策略导致的振荡(大→小→大→小)使系统频繁切换工作模式,损失了批次间的效率稳定性。SlidingChunker 的稳定分配能保持 GPU 长期在高效工作区运行。

2.2 请求顺序的隐性影响

即使块大小确定后,请求的服务顺序对 SLO 也有显著影响:

  • 纯 FCFS(先到先服务): 长 prompt 请求独占预填充预算,阻塞更紧迫的后来请求。
  • 纯 EDF(最早截止时间优先): 忽视了不同请求完成预填充所需的实际工作量,可能把大量 budget 分给剩余量极大但截止时间尚早的请求。
  • 纯 SJF(最短任务优先): 短请求抢占即将截止的长请求,反而拉高了高工作量请求的违约率。

理想的排序应该综合考虑:SLO 保护状态、截止时间紧迫度、剩余预填充工作量三个维度——这正是 SlidingServe 多级优先级排序器的设计目标。

3. SlidingServe 系统架构

SlidingServe 作为一个调度层叠加在 vLLM 之上,以每次迭代的系统状态为输入,输出当前轮的批次分配方案,形成闭环决策回路。

graph TD
    subgraph 请求队列
        WQ[等待队列\nWaiting Queue]
        PQ[预填充队列\nPrefilling Queue]
        DQ[解码队列\nDecoding Queue]
    end

    subgraph SlidingServe 调度器
        PS[多级优先级排序器\nMLPS]
        CB[候选批次构建\n贪婪填充]
        BLP[批次延迟预测器\nBatch Latency Predictor]
        VC{违约检查器\nViolation Checker}
        SC[SlidingChunker\n三分搜索最优块大小]
        BCon[BatchConstructor\n0/1 背包 DP]
    end

    WQ --> PS
    PQ --> PS
    DQ --> BLP
    DQ --> PS
    PS --> CB
    CB --> VC
    BLP --> VC
    VC -- 无违约风险 --> SC
    VC -- 存在违约风险 --> BCon
    SC --> EB[可执行批次 A*]
    BCon --> EB
    EB --> GPU[GPU 执行]
    GPU --> WQ
    GPU --> PQ
    GPU --> DQ

图2:SlidingServe 整体架构。调度器通过违约检查器分叉:无违约走 SlidingChunker 路径(优化块大小),有违约走 BatchConstructor 路径(精选请求子集)。两条路径均输出统一格式的批次分配 A*。

三个基础队列:

  1. 等待队列(Waiting Queue): 新到达、尚未开始预填充的请求。
  2. 预填充队列(Prefilling Queue): 正在进行分块预填充的请求(可能跨越多个迭代)。
  3. 解码队列(Decoding Queue): 已完成预填充、正在逐 token 生成的请求。

每轮调度流程:

  1. MLPS 对等待和预填充队列中的请求重新排序;
  2. 根据当前块大小和排好序的请求队列,构建最大候选批次(不直接执行,仅用于风险评估);
  3. 违约检查器结合预测器估计的执行时间和解码请求的 TBT 余量,判断直接执行该候选批次是否会引发 TTFT 违约;
  4. 若无违约风险 → SlidingChunker 优化块大小;若有违约风险 → BatchConstructor 筛选请求子集;
  5. 形成最终可执行批次,提交 GPU 执行;
  6. 执行完成后更新三个队列,进入下一轮调度。

4. 核心技术组件深度解析

4.1 批次延迟预测器(Batch Latency Predictor)

预测器是整个系统的基础设施:SlidingChunker 的三分搜索和 BatchConstructor 的 DP 都依赖它快速评估不同批次配置的执行时间。预测器的设计目标是”足够准确且极度轻量”——每次调度轮次需要被调用数十次,不能成为瓶颈。

输入表示。 给定候选批次 B={(ci,ui)}i=1n\mathcal{B} = \{(c_i, u_i)\}_{i=1}^n,其中 cic_i 是分配给请求 ii 的 token 数,uiu_i 是其已缓存 token 数(KV 缓存长度):

D={ici1},P={ici>1}(4)\mathcal{D} = \{i \mid c_i \leq 1\}, \quad \mathcal{P} = \{i \mid c_i > 1\} \tag{4}

批次场景分类:

s(B)={pure decode,P=0pure prefill,D=0mixed,otherwise(5)s(\mathcal{B}) = \begin{cases} \text{pure decode}, & |\mathcal{P}| = 0 \\ \text{pure prefill}, & |\mathcal{D}| = 0 \\ \text{mixed}, & \text{otherwise} \end{cases} \tag{5}

7 维特征向量。 预测器采用显式特征工程,将影响批次延迟的主因分解为预填充开销、解码开销、缓存状态、全局负载和交互项:

特征数学定义语义
x1x_1iPci(ui+ci)\sum_{i \in P} c_i(u_i + c_i)预填充阶段的注意力计算复杂度(跨 chunk 与缓存的交叉项)
x2x_2iPci2\sum_{i \in P} c_i^2预填充 chunk 内的自注意力强度
x3x_3i=1nui\sum_{i=1}^n u_i总缓存 token 数(全局内存压力)
x4x_4D\lvert\mathcal{D}\rvert解码请求数量
x5x_5iDui\sum_{i \in D} u_i解码请求的总上下文长度(决定 KV 缓存访存量)
x6x_6iPci\sum_{i \in P} c_i当前轮预填充 token 总量
x7x_7maxiPci\max_{i \in P} c_i单个预填充请求的最大 token 分配量

预测模型(场景 mm 下的线性回归):

T^=bˉ(m)+j=17wˉj(m)xj(6)\hat{T} = \bar{b}^{(m)} + \sum_{j=1}^{7} \bar{w}_j^{(m)} x_j \tag{6}

为什么用线性模型? 一方面,手工设计的特征(x1,x2x_1, x_2 捕获注意力的二次代价,x3x_3x5x_5 捕获内存带宽压力)已经编码了主要的非线性;另一方面,线性模型的推理时间远低于 1 μs,可以在一轮调度中被调用数十次而无明显开销。

特征工程的物理意义。 每个特征的设计都有明确的硬件动机:

  • x1=iPci(ui+ci)x_1 = \sum_{i\in P} c_i(u_i + c_i) 在因果自注意力中,大小为 cic_i 的 chunk 需要与 uiu_i 个已缓存位置及 chunk 内 cic_i 个位置做全注意力计算,工作量正比于 ci(ui+ci)c_i \cdot (u_i + c_i)。对所有预填充请求求和得到本轮批次预填充注意力的总 FLOP 估算。当批次以预填充为主时,x1x_1 是预测误差最小的特征。

  • x2=iPci2x_2 = \sum_{i\in P} c_i^2 chunk 内部自注意力贡献 O(ci2)O(c_i^2) 操作,与 KV 缓存深度无关。保留 x2x_2 使回归模型能够对”缓存无关的 chunk 内计算”和”跨缓存的注意力”赋予不同权重。

  • x3x_3x5x_5 解码请求每步只产生 1 个 token,但需要从 HBM 加载完整的 KV 缓存。x3x_3 捕获总内存压力,x5x_5 捕获解码请求的具体内存访问量。

  • x6,x7x_6, x_7 总预填充 token 数是体量指标;单请求最大分配量捕获批次中可能触发特殊内存分配事件的极端情况。

线性模型在 R² > 0.99 下有效,原因是 GPU 算子执行时间在固定硬件配置下近似分段线性于这些 FLOP/内存访问量——主要的非线性已经被手工特征显式捕获。

训练策略: 离线初始化(基于历史剖析数据)+ 在线增量更新(在后台线程异步采集新样本,定期热替换模型)。对三种场景分别训练专家模型,当某场景样本量不足时回退到全局模型。

实测精度(Qwen2.5-7B, RTX 3090 TP2): MAE ≤ 2.72 ms,RMSE ≤ 4.33 ms,R² > 0.99。在典型的 10–50 ms 迭代时间尺度下,这个精度已经足以支持可靠的调度决策。

4.2 SlidingChunker:滑动窗口动态分块

SlidingChunker 是论文的核心创新。它把”当前轮用多大的块”这个问题从单步最大化升级为”当前轮 + 下一轮”的联合最优化。

窗口约束计算:

当前窗口可用时间(由最紧迫的解码请求 TBT 余量决定):

Tcur=miniDsafesi(t)(7)T_{\text{cur}} = \min_{i \in \mathcal{D}_{\text{safe}}} s_i(t) \tag{7}

下一窗口估计可用时间:

Tnext=miniDsafe(si(t)Tcur+Litbt)(8)T_{\text{next}} = \min_{i \in \mathcal{D}_{\text{safe}}} \left(s_i(t) - T_{\text{cur}} + L_i^{\text{tbt}}\right) \tag{8}

其中 si(t)=di,kts_i(t) = d_{i,k} - t 是解码请求 ii 的当前 TBT 余量,LitbtL_i^{\text{tbt}} 是其 TBT SLO。

总预算与最优分配:

先用二分搜索求解满足两轮约束的总 token 预算 BΣB_\Sigma,然后用三分搜索[1,BΣ][1, B_\Sigma] 上找到最优分割 BB^*

B=argminb[T^(b)+T^(BΣb)](9)B^* = \arg\min_b \left[\hat{T}(b) + \hat{T}(B_\Sigma - b)\right] \tag{9}

T^()\hat{T}(\cdot) 是凸函数时(变换器推理的执行时间通常满足此性质),目标函数 T^(b)+T^(BΣb)\hat{T}(b) + \hat{T}(B_\Sigma - b) 是单峰的,三分搜索在 O(logBΣ)O(\log B_\Sigma) 步内收敛(通常 < 20 次预测器调用)。

算法1:SlidingChunker
输入:D(解码请求集),Q(MLPS 排好序的候选请求队列),
       B_max(服务器最大块大小),t(当前调度时刻),
       F(BatchForwarder,封装预测器)
输出:B*(最优块大小),A*(请求级分配方案)

1. T_cur ← min_{i∈D_safe} s_i(t)          // 最紧迫解码请求的 TBT 余量
2. T_next ← min_{i∈D_safe} (s_i(t) - T_cur + L_i^tbt)  // 下一窗口约束

3. 用二分搜索求 B_Σ = TimeToBudget(T_cur + T_next)

4. 三分搜索 b ∈ [1, B_Σ]:
       找到 B* = argmin_b [F.predict(batch, chunk=b) + F.predict(batch, chunk=B_Σ-b)]

5. 按 B* 从排好序的队列 Q 中贪婪填充,得到 A*

6. 返回 (B*, A*)

直观理解: 假设当前轮截止时间 100 ms,下一轮截止时间 50 ms。贪婪策略用满 100 ms,导致下一轮只剩 50 ms 余量,对应块大小 260 token。SlidingChunker 发现,在当前轮少用 20 ms(80 ms),下一轮可以用 70 ms,两轮合计可以多处理约 100 个 token——这就是滑动窗口的价值所在。

4.3 多级优先级排序器(Multi-Level Priority Sorter, MLPS)

MLPS 在每轮迭代开始时对所有候选请求(预填充队列 + 等待队列)进行重新排序。解码请求始终优先(每轮固定分配 1 个 token,计入基础预算),剩余预算按排好序的队列依次分配。

核心指标推导:

剩余预填充 token 数:

ri(t)=pici(t)(10)r_i(t) = p_i - c_i(t) \tag{10}

TTFT 余量(Slack):

si(t)=ai+Littftt(11)s_i(t) = a_i + L_i^{\text{ttft}} - t \tag{11}

基于近期系统吞吐量 ρt\rho_t 估算完成预填充所需时间:

T^ipre(t)=ri(t)ρt(12)\hat{T}_i^{\text{pre}}(t) = \frac{r_i(t)}{\rho_t} \tag{12}

归一化紧迫度(urgency ratio):

ui(t)=T^ipre(t)max(si(t),ϵ)=ri(t)ρtmax(si(t),ϵ)(13)u_i(t) = \frac{\hat{T}_i^{\text{pre}}(t)}{\max(s_i(t), \epsilon)} = \frac{r_i(t)}{\rho_t \cdot \max(s_i(t), \epsilon)} \tag{13}

ui(t)>1u_i(t) > 1 时,意味着即使以当前吞吐率不停工作,也无法在截止时间前完成预填充——该请求已经处于”存活边界”。紧迫标志:

ei(t)=1[ui(t)>α](14)e_i(t) = \mathbf{1}[u_i(t) > \alpha] \tag{14}

三层优先级键(字典序排列):

Ki(t)=(1gi, 1ei(t), ri(t))(15)K_i(t) = \left(1 - g_i,\ 1 - e_i(t),\ r_i(t)\right) \tag{15}

Ki(t)K_i(t) 升序排列(值小的优先):

Qt=LexSortiPt ⁣(1gi, 1ei(t), ri(t))(16)Q_t = \text{LexSort}_{i \in \mathcal{P}_t}\!\left(1-g_i,\ 1-e_i(t),\ r_i(t)\right) \tag{16}

三层含义:

  1. 第一层(SLO 保护标志 gig_i): 被标记为需要特殊保护的请求(如高优先级用户、已处于流式解码状态的请求)排在最前。
  2. 第二层(紧迫度 ei(t)e_i(t)): 在同等保护级别内,紧迫请求(ei=1e_i = 1)优先于非紧迫请求,防止即将超时的请求被低风险请求抢占。
  3. 第三层(剩余工作量 ri(t)r_i(t)): 在同等紧迫度内,剩余 token 数少的请求优先——类似最短作业优先(SJF),在有限预算内最大化完成预填充的请求数量。
算法2:多级优先级排序
输入:P_t(预填充+等待请求集),ρ_t(近期吞吐量),
       t(当前时刻),α(紧迫阈值),ε(数值稳定常数)
输出:Q_t(排好序的候选队列)

对每个请求 i ∈ P_t:
    r_i(t) ← p_i - c_i(t)
    s_i(t) ← a_i + L_i^ttft - t
    û_i(t) ← r_i(t) / (ρ_t · max(s_i(t), ε))
    e_i(t) ← 1 if û_i(t) > α else 0
    K_i(t) ← (1 - g_i, 1 - e_i(t), r_i(t))

返回 按 K_i(t) 升序排列的 P_t

4.4 BatchConstructor:基于动态规划的批次精构

当违约检查器发现候选批次的预测执行时间会导致某些请求 TTFT 违约时,BatchConstructor 接管。它不是简单地丢弃所有有风险的请求,而是将批次构建转化为一个有容量约束的请求选择问题(0/1 背包)

关键参数推导:

对每个”风险请求(risky request)” aa(其 TTFT 余量小于预测批次执行时间),将其作为”锚点(anchor)”:

可用 token 容量(aa 的 TTFT 余量对应的 token 预算,减去解码请求固定消耗):

Ca=BaD(17)C_a = B_a - |\mathcal{D}| \tag{17}

其中 Ba=TimeToBudget(sa)B_a = \text{TimeToBudget}(s_a)sas_a ms 内能处理的最大 token 数。

强制将锚点 aa 纳入批次(因为它最需要立即处理)。对候选请求 jjsjsas_j \geq s_a,即余量不比锚点更紧)定义:

价值函数: 综合考虑余量紧迫性和剩余工作量:

vj=1sjkSask+rjkSark(18)v_j = \frac{1}{\dfrac{s_j}{\sum_{k \in S_a} s_k} + \dfrac{r_j}{\sum_{k \in S_a} r_k}} \tag{18}

这是一个调和均值型公式:余量相对小(更紧迫)且剩余工作量相对少(更容易完成)的请求价值更高。

0/1 背包 DP 公式:

maxYSa{a}jYvjs.t.jYrjCara(19)\max_{\mathcal{Y} \subseteq S_a \setminus \{a\}} \sum_{j \in \mathcal{Y}} v_j \quad \text{s.t.} \quad \sum_{j \in \mathcal{Y}} r_j \leq C_a - r_a \tag{19}

DP 递推关系(以请求为物品,剩余 token 数 rjr_j 为重量,vjv_j 为价值,背包容量 CaraC_a - r_a):

dp[j][c]=max ⁣{dp[j1][c](skip request j)dp[j1][crj]+vj(take request j, crj)(20)\text{dp}[j][c] = \max\!\begin{cases} \text{dp}[j-1][c] & \text{(skip request } j \text{)} \\ \text{dp}[j-1][c - r_j] + v_j & \text{(take request } j,\ c \geq r_j \text{)} \end{cases} \tag{20}

锚点选择比较规则: 对每个锚点 aa 求解 DP,得到选集 Ya=Y{a}\mathcal{Y}_a = \mathcal{Y}^* \cup \{a\}。通过字典序比较选出最优方案:

Y1Y2if(Y1, vj, rj)>lex(Y2, vj, rj)(21)\mathcal{Y}_1 \succ \mathcal{Y}_2 \quad \text{if} \quad \left(|\mathcal{Y}_1|,\ \sum v_j,\ \sum r_j\right) >_{\text{lex}} \left(|\mathcal{Y}_2|,\ \sum v_j,\ \sum r_j\right) \tag{21}

优先最大化”完成预填充的请求数”,其次最大化”总价值”,最后最大化”总 token 预算使用”。

最终批次分配:

A={(i,1)iD}{(j,rj)jY}(22)A^* = \{(i, 1) \mid i \in \mathcal{D}\} \cup \{(j, r_j) \mid j \in \mathcal{Y}^*\} \tag{22}

每个解码请求分配 1 个 token;每个被选中的预填充请求分配其全部剩余 token(让它在本轮完成预填充,立即生成首 token)。

算法3:BatchConstructor
输入:candidate_batch(MLPS 排好序的候选批次),
       predictor(延迟预测器),D(解码请求集)
输出:A*(最优批次分配),或 None(无违约,无需干预)

1. T̂_full ← predictor.predict(candidate_batch)
2. 若所有预填充请求的 TTFT 余量 ≥ T̂_full:返回 None

3. risky ← {i ∈ prefill_requests : s_i(t) < T̂_full}
4. best ← None

5. 对每个锚点 a ∈ risky:
    a. B_a ← TimeToBudget(s_a);C_a ← B_a - |D|
    b. 若 r_a > C_a:跳过(锚点本身超出容量,不可行)

    c. S_a ← {j ∈ candidates : s_j ≥ s_a, j ≠ a}
    d. 计算每个 j ∈ S_a 的 v_j(式18)

    e. // 0/1 背包 DP
       capacity ← C_a - r_a
       dp[0..n][0..capacity] ← 0
       对 j = 1..|S_a|:
           对 c = 0...capacity:
               dp[j][c] ← dp[j-1][c]
               若 c ≥ r_{S_a[j]}:
                   dp[j][c] ← max(dp[j][c], dp[j-1][c - r_{S_a[j]}] + v_{S_a[j]})
       Y* ← 回溯 dp 得到选集
       Y_a ← Y* ∪ {a}

    f. 若 best == None 或 Y_a ≻ best:best ← Y_a

6. A* ← {(i,1)|i∈D} ∪ {(j, r_j)|j∈best}
7. 返回 A*

5. 调度算法全流程追踪

用一个具体的例子端到端地追踪一次完整调度迭代。

场景设置(t=50t = 50 ms):

  • 解码队列 D\mathcal{D}:8 个请求,最紧迫 TBT 余量 sminD=30s_{\min}^D = 30 ms
  • 预填充队列:r1(剩余 500 token,TTFT 余量 40 ms),r2(800 token,120 ms),r3(200 token,80 ms)
  • 等待队列:r4(1024 token,200 ms),r5(300 token,60 ms)
  • 近期吞吐量 ρt=5000\rho_t = 5000 token/s

这个例子展示了四个组件如何协同工作:MLPS 决定候选集的顺序,BatchConstructor 在违约场景下精确筛选请求,预测器为所有延迟估算提供基础,而 SlidingChunker 在下一轮(无违约时)负责优化块大小。组件间的依赖关系清晰:BatchConstructor 的决策依赖 MLPS 排序的候选集;SlidingChunker 的下一轮估算依赖预测器的精度。

Step 1 — MLPS 排序: 计算各请求的紧迫度 uiu_i

  • r1:u=500/(5000×0.04)=2.5u = 500/(5000 \times 0.04) = 2.5 → 紧迫
  • r2:u=800/(5000×0.12)=1.33u = 800/(5000 \times 0.12) = 1.33 → 紧迫
  • r3:u=200/(5000×0.08)=0.5u = 200/(5000 \times 0.08) = 0.5 → 不紧迫
  • r4:u=1024/(5000×0.2)1.02u = 1024/(5000 \times 0.2) \approx 1.02 → 边界
  • r5:u=300/(5000×0.06)=1.0u = 300/(5000 \times 0.06) = 1.0 → 边界

Ki=(1gi,1ei,ri)K_i = (1-g_i, 1-e_i, r_i) 升序排列 → [r1(紧, 500), r2(紧, 800), r5, r4, r3]

Step 2 — 候选批次构建: 贪婪填充到 Bmax=2048B_{\max} = 2048:r1(500) + r2(800) + r3(200) + r5(300) + 8 解码 = 1808 token。

Step 3 — 违约检查: 预测器评估:T^full=45\hat{T}_{\text{full}} = 45 ms。r1 余量 40 ms < 45 ms → 违约! 路由到 BatchConstructor。

Step 4 — BatchConstructor(锚点 = r1): Br1=TimeToBudget(40ms)=1600B_{r1} = \text{TimeToBudget}(40\text{ms}) = 1600Cr1=16008=1592C_{r1} = 1600 - 8 = 1592,剩余容量 =1592500=1092= 1592 - 500 = 1092。 候选集 Sr1S_{r1}:{r2(800), r3(200), r5(300)}(余量 ≥ 40 ms)。 背包 DP(容量 1092):r3(200) + r5(300) + r2(800=692 < 1092-200-300=592? 不满足)→ 选 {r3, r5},总重量 500 ≤ 1092。 最终批次:r1(500) + r3(200) + r5(300) + 8 解码 = 1008 token。 验证:T^=32\hat{T} = 32 ms < 40 ms ✓,r2、r4 推迟到下一轮。

Step 5 — GPU 执行: r1、r3、r5 完成预填充并立即生成首 token,进入解码队列。

Step 6 — 下一轮(SlidingChunker 路径): 违约检查器发现无违约风险。SlidingChunker 计算 Tcur=30T_{\text{cur}} = 30 ms,TnextT_{\text{next}},三分搜索得 B=1200B^* = 1200 token,比贪婪策略的 Cgreedy=1400C_{\text{greedy}} = 1400 略小,但为下一轮保留了足够余量。

flowchart TD
    S([迭代开始]) --> MLPS[MLPS:对所有候选请求\n计算紧迫度并排序]
    MLPS --> CB[贪婪填充候选批次\n至 B_max]
    CB --> VC{违约检查器\n预测 T̂_full}
    VC -- T̂_full ≤ 所有余量\n无违约 --> SC[SlidingChunker\n三分搜索 B*]
    VC -- T̂_full > 某请求余量\n有违约 --> BCon[BatchConstructor\n0/1 背包 DP]
    SC --> EB[可执行批次 A*]
    BCon --> EB
    EB --> GPU[GPU 前向传播]
    GPU --> UPD[更新三个队列\n记录指标]
    UPD --> S

图3:SlidingServe 单次迭代完整流程图。违约检查器是关键分叉点,两条路径输出统一格式的批次分配 A*,对 GPU 执行层透明。

5.2 BatchConstructor 的组合优化视角

BatchConstructor 的问题本质是一个带约束的集合选择问题:从候选请求中选出一个子集,使其满足批次执行时间约束(不超过锚点的 TTFT 余量),同时最大化”调度价值”(完成更多预填充、优先服务紧迫请求)。

这与经典的 0/1 背包问题完全同构:

  • 物品 = 候选请求(价值 vjv_j,重量 rjr_j
  • 背包容量 = CaraC_a - r_a(去掉锚点和解码请求占用后的剩余 token 容量)
  • 目标 = 最大化所选请求的总价值 vj\sum v_j

0/1 背包是弱 NP-hard 问题(用伪多项式时间 DP 求解),在实践中完全可行:Sa50|S_a| \leq 50Ca4096C_a \leq 4096,DP 表大小约 200K 项,CPU 上 < 1 ms 即可计算完毕。与之相对,贪婪启发式(按 vj/rjv_j/r_j 降序选取,分数背包的最优策略)对 0/1 背包最坏情况下只有 2-近似保证。SlidingServe 选择精确 DP 而非贪婪近似,体现了”在问题规模可控时优先保证最优性”的工程判断。

6. 理论分析

6.1 滑动窗口优于贪婪的形式化论证

f(b)f(b) 为执行 bb 个 token 批次的时间(单调不减、在大块大小时呈凸性,由注意力的二次代价主导)。

贪婪策略最大化 b1b_1 使 f(b1)Tcurf(b_1) \leq T_{\text{cur}},下一轮仅能用 b2=BΣb1b_2 = B_\Sigma - b_1

SlidingChunker 的目标:

B=argminb[f(b)+f(BΣb)](23)B^* = \arg\min_b [f(b) + f(B_\Sigma - b)] \tag{23}

ff 是凸函数时,f(b)+f(BΣb)f(b) + f(B_\Sigma - b)b=BΣ/2b = B_\Sigma/2 处取最小值。贪婪解 bgreedy=TimeToBudget(Tcur)b_{\text{greedy}} = \text{TimeToBudget}(T_{\text{cur}}) 通常不等于 BΣ/2B_\Sigma/2,因此是次优的。特别地,当 TcurTnextT_{\text{cur}} \gg T_{\text{next}}(当前余量远大于下一轮)时,贪婪策略严重”超消费”当前窗口,导致下一轮极端受限。

6.2 BatchConstructor 的时间复杂度

设风险请求数为 nrn_r,候选请求数为 ncn_c,最大背包容量为 CmaxC_{\max}

TBatchConstructor=O(nrncCmax)(24)T_{\text{BatchConstructor}} = O(n_r \cdot n_c \cdot C_{\max}) \tag{24}

在实践中:nrncn_r \ll n_c(稳态下违约较少),ncn_c 通常为 10–50,CmaxC_{\max} 受最大块大小限制。论文未给出此复杂度的分析,也未讨论持续过载时 nrncn_r \to n_c 的退化情形,这是一个值得关注的问题。

6.3 吞吐量与 SLO 达标率的权衡模型

有效吞吐量(Goodput)上界:

λE[requests completed per iter]E[Titer](25)\lambda \leq \frac{\mathbb{E}[\text{requests completed per iter}]}{\mathbb{E}[T_{\text{iter}}]} \tag{25}

SlidingChunker 提升分子(+16.7% 批次大小),BatchConstructor 改善分母的效率(减少因违约导致的无效等待),MLPS 优化两者的分配效率(+5.7%)。三个组件的效果有正向协同:消融实验显示三者合计提升 27.8%,略高于单独相加(16.7% + 5.7% + 5.4% = 27.8%,此处恰好相当)。

7. 实验与结果

7.1 实验设置

维度配置
模型Llama3-8B、Qwen2.5-7B
硬件NVIDIA RTX 3090 × 2,张量并行度 TP2
基础框架vLLM
数据集ShareGPT(对话),arXiv-Summarization-v1/v2(长文摘要),mixed-v1(ShareGPT:arXiv-v1 = 3:1),mixed-v2(5:1)
基线Sarathi-EDF(EDF 策略 + Sarathi-Serve);QoServe(ASPLOS 2026,SOTA)
主要指标Goodput(req/s 在 ≤1% SLO 违约率下);SLO 违约率;p50/p90/p99 TTFT 和 TBT

7.2 关键结果概览

Goodput 对比(图4): SlidingServe 相比 Sarathi-EDF 提升 25%–111%,相比 QoServe 提升 9.7%–30%。长 prompt 数据集(arXiv)收益最大,短 prompt(ShareGPT)收益最小。这与预期一致:分块预填充在长 prompt 下需要多轮迭代,SlidingChunker 的跨迭代优化价值更高。

高负载下的 SLO 违约(图5): 在 2× goodput 点的高负载下,SlidingServe 的 SLO 违约率比 QoServe 最高低 53%。p50/p90 TTFT 与 QoServe 相当,但 p99 尾延迟偶尔更高——这是有意的权衡:已超时的请求被降低优先级,资源优先保留给仍有机会达标的请求。

瞬态过载场景(图6,Section 5.3):

对比SlidingServe 违约率降低
vs. Sarathi-EDF30.22%
vs. QoServe23.74%

消融实验(Table 4,Qwen2.5-7B,mixed-v1):

配置吞吐量提升(vs. Sarathi-EDF)说明
Sarathi-EDF(基线)0%
+SlidingChunker+16.7%跨迭代优化贡献最大
+SlidingChunker +MLPS+22.4%排序优化额外 5.7%
全量 SlidingServe+27.8%BC 再加 5.4%

图4:消融结果汇总。SlidingChunker 是吞吐量的主要贡献者;MLPS 和 BatchConstructor 在高负载下对 SLO 违约率的降低贡献更突出。

预测器精度(Table 5):

GPU 配置MAE (ms)RMSE (ms)
RTX 3090 (TP1)2.524.12> 0.99
RTX 3090 (TP2)2.724.33> 0.99

图5:批次延迟预测器的精度。在 10–50 ms 的典型迭代时间尺度上,< 3 ms 的 MAE 已经足够支撑可靠的调度决策。

7.5 与 QoServe 的深度对比

QoServe(ASPLOS 2026)是论文中最接近的基线,也是自称的 SOTA。两者的关键差异:

能力QoServeSlidingServe
延迟预测器无(基于粗粒度统计估算)有(7 维线性模型,R²>0.99)
跨迭代优化无(单步动态分块)有(滑动窗口 2 步联合优化)
批次内请求选择无(填满或降级)有(0/1 背包 DP)
主动降级机制有(proactive relegation)无(通过 MLPS 的优先级降低)

QoServe 的”主动降级”将已超时的请求移出活跃批次,减少对其他请求的干扰——这是一个有价值的思路,但只在违约已经发生后才起作用(事后补救)。SlidingServe 的 BatchConstructor 是事前预防:在执行之前就预测批次是否会导致违约,并在执行前完成选择——从根本上避免某些违约的发生。

这两个机制并不互斥,结合使用可能产生更好的效果,但论文未探索这一方向。

8. 与现有工作对比

graph LR
    subgraph 纯吞吐量
        ORCA[Orca\n连续批处理]
        vLLM[vLLM\nPagedAttention]
    end
    subgraph 分块预填充
        SARATHI[Sarathi-Serve\n分块预填充]
        SEDF[Sarathi-EDF\n+EDF 优先级]
    end
    subgraph SLO 感知
        QOS[QoServe\n混合优先级+主动降级]
        SS[SlidingServe\n滑动窗口+DP 批次选择]
        SOLA[SOLA\n状态感知调度]
        POLY[PolyServe\n负载梯度路由]
    end
    ORCA --> vLLM --> SARATHI --> SEDF --> QOS --> SS

图6:LLM 服务调度器的演化谱系。SlidingServe 在 QoServe 的基础上增加了滑动窗口跨迭代优化和 DP 批次内选择两个关键能力。

系统分块预填充SLO 感知跨迭代优化批次内 DP延迟预测器
Orca (2022)
vLLM (2023)
Sarathi-Serve (2023/24)局部
Sarathi-EDF✓(EDF)
QoServe (2026)部分
SlidingServe (2026)✓(窗口=2)✓(0/1背包)✓(线性)

与 QoServe 最接近,但有两个关键区别:(1) SlidingServe 做了显式的两步联合优化,而 QoServe 依赖启发式的”主动降级(proactive relegation)”;(2) SlidingServe 的 BatchConstructor 解决了 QoServe 未处理的批次内 TTFT 干扰问题。

7.3 各数据集的 Goodput 差异分析

不同数据集上的 Goodput 提升幅度差异显著,背后有清晰的调度理论解释:

  • ShareGPT(短 prompt,对话): vs. Sarathi-EDF +25–35%;vs. QoServe +9.7–12%。短 prompt 通常一次完成预填充,SlidingChunker 的跨迭代优化无用武之地;MLPS 的紧迫度分化效果也有限(所有请求相似)。

  • arXiv-Summarization(长 prompt,单文档摘要): vs. Sarathi-EDF +80–111%;vs. QoServe +22–30%。长 prompt 预填充需要跨越多轮迭代,这正是 SlidingChunker 发挥作用的场景。贪婪策略在第一轮”超消费”时间槽,导致后续迭代效率低下;SlidingChunker 的平衡分配使每轮迭代都在高效区间运行。

  • Mixed-v1/v2(混合负载): 介于两者之间。MLPS 需要在短 prompt(紧迫但工作量小)和长 prompt(余量充裕但工作量大)之间动态分配优先级,BatchConstructor 则专门处理长 prompt 请求阻塞短 prompt 请求的情形。

这种数据集敏感性是 SlidingServe 设计的自然结果:它为长 prompt 工作负载针对性优化,在日益重要的文档理解、代码审查、长文摘要等场景中价值最大。

7.4 实现细节与隐性开销

TimeToBudget 的逆解计算。 SlidingChunker 和 BatchConstructor 都依赖 TimeToBudget(T) 函数:给定延迟目标 TT,求最大满足 T^(b)T\hat{T}(b) \leq T 的块大小 bb。对线性预测器而言,在单一场景下这可以解析求解,但混合场景的场景切换(式 5)使得必须用二分搜索(O(logBmax)O(\log B_{\max}) 次预测器调用,通常 12–15 次)。

多队列管理开销。 三个队列需要在每次迭代中维护请求的完整元数据(到达时间、SLO 参数、累计 token 计数、保护标志、紧迫度估计)。全量重排序是 O(NlogN)O(N \log N) 的,对于 N=1000N = 1000 并发请求,每轮调度约 20 μs——在 10–50 ms 的迭代时间内可以忽略,但论文未讨论是否用增量数据结构(如锦标赛树)优化。

9. 批判性分析:不足与可改进之处

9.a 主要弱点与缺陷

1. 评估规模严重受限。 论文全部实验在 2× RTX 3090 (TP2) 上完成。RTX 3090 是消费/半专业级显卡,其计算/内存带宽比与生产环境的 A100/H100 差异显著。论文没有任何证据表明 7 维特征预测器能推广到不同 GPU 架构。x1,x2x_1, x_2 以原始 token 计数近似注意力计算量,但在 FlashAttention-2/3 的具体实现下,有效带宽特性会有质的变化。

2. 只测试了两个 7B 规模的模型。 Llama3-8B 和 Qwen2.5-7B 都是参数量接近的”小模型”。对于 70B+ 的大模型(在生产中更常见),内存带宽是更主要的瓶颈,块大小与延迟的关系会有本质不同,现有预测器特征可能失效。论文完全没有讨论对更大模型的可扩展性。

3. BatchConstructor 的计算开销未定量分析。 DP 的复杂度为 O(nrncCmax)O(n_r \cdot n_c \cdot C_{\max}),在持续过载时 nrn_r 可能接近 ncn_c,DP 可能成为调度瓶颈。论文仅说预测器校准”不阻塞主推理路径”,但对 BatchConstructor 本身在 CPU 上的延迟没有给出任何测量数据。在 10–50 ms 的迭代时间尺度上,即使 1 ms 的调度开销也是有意义的。

4. 基线选择不完整。 论文仅与 Sarathi-EDF 和 QoServe 对比,未覆盖 SOLA (MLSys 2025)、PolyServe (2025)、WindServe (ISCA 2025) 等同期 SLO 感知调度器。声称”相比先进调度系统最高提升 30%“的说法基于仅一个真正有竞争力的基线,过于局限。

5. 滑动窗口大小固定为 2,无消融。 论文未分析窗口大小 3 或 4 是否会带来更多收益,也未证明窗口 2 是最优选择。这个超参数的选取缺乏实验支撑,仅凭直觉。

6. SLO 类型单一。 系统仅处理 TTFT 和 TBT SLO,不支持端到端总生成时间 SLO、多租户差异化 SLO,也不支持输出长度约束。生产环境中往往有更复杂的 SLO 结构。

9.b 论文轻描淡写的局限性

短 prompt 场景的实际收益不明。 论文提及 ShareGPT 上收益”较少”,但没有量化 SlidingChunker(三分搜索 + 多次预测器调用)在短 prompt 场景引入的额外 CPU 开销是否抵消了任何收益。如果调度开销本身与迭代时间相当,SlidingServe 在纯对话场景下可能反而更慢。

在线预测器校准的时效性问题。 背景线程异步更新预测器,但论文未披露校准周期。如果负载在校准周期内剧变(例如从 ShareGPT 突然切换到 arXiv 风格的请求),预测器可能在一段时间内严重失准,影响调度质量。

与投机解码(Speculative Decoding)的兼容性。 投机解码彻底改变了每 token 的执行模型(单步可产生多个 token),而 SlidingServe 的 TBT 模型(公式 1)假设每步严格生成一个 token。这两者的集成需要非平凡的修改,论文完全未讨论。

9.c 具体改进建议

  1. 将硬件无关的 FLOP 估算纳入特征:x1=FLOPs(b,u)/GPU peak TFLOPSx_1' = \text{FLOPs}(b, u) / \text{GPU peak TFLOPS} 替代原始 token 计数,提升跨硬件泛化能力,无需为每块 GPU 重新训练。

  2. 增加窗口大小消融: 对窗口大小 1(退化为贪婪)、2(当前实现)、3、4 分别测试,确认两步近似是否接近最优。

  3. 量化 BatchConstructor 的 CPU 开销: 在每次迭代中记录调度器 CPU 耗时,分解为 MLPS、预测器、SlidingChunker、BatchConstructor 各部分,确认总开销 < 迭代时间的 5%。

  4. 在 A100/H100 上验证: 在数据中心级 GPU 上复现主要结果,或至少提供初步证据表明预测器在不同硬件上的泛化能力。

  5. 纳入输出长度分布预测: 利用历史请求的输出长度分布预测未来解码队列压力,改善 SlidingChunker 中 TnextT_{\text{next}} 的估计精度。

  6. 与 SOLA、WindServe、PolyServe 进行对比: 完整覆盖 2025 年的 SLO 感知调度系统,增强结论的可信度。

10. 局限性与边界条件

SlidingServe 发挥最佳的场景:

  • 长 prompt 负载(arXiv 摘要等),预填充跨越多轮迭代,滑动窗口优化价值高。
  • 异构混合负载,请求的 TTFT/TBT SLO 差异大,MLPS 的多级排序效果明显。
  • 中高负载(接近 goodput 上界),违约风险足够高,BatchConstructor 的收益明显。

SLO 违约率 vs. 吞吐量的 Pareto 前沿。 实验中一个关键观察:SlidingServe 不仅仅是在吞吐量和 SLO 合规性之间做权衡——它扩展了 Pareto 前沿。在相同 SLO 违约率(≤1%)下,SlidingServe 实现更高吞吐量;在相同吞吐量下,它实现更低违约率。这只有通过减少”调度浪费”才可能做到:SlidingChunker 减少因块大小次优导致的迭代效率损失;MLPS 减少预算分配给已无法达标的请求;BatchConstructor 在执行前阻止批次级违约。

贪婪调度无法达到 Pareto 最优的根本原因:它最大化局部目标(当前迭代吞吐量),与全局目标(多轮迭代的 Goodput)不一致。SlidingServe 的两步联合优化是打破局部最优陷阱所需的最小扩展,且没有引入指数级复杂度。

与 KV 缓存淘汰的交互问题。 当 GPU 显存不足时,vLLM 等系统会主动将部分请求的 KV 缓存换出到 CPU 内存或重新计算(KV 缓存抢占/淘汰)。SlidingServe 的调度循环不感知内存压力——它的 SlidingChunker 和 BatchConstructor 假设所有队列中的请求都已有 KV 缓存驻留于 GPU。在极端内存压力下,调度器可能做出与内存管理器冲突的决策,导致批次中途发生意外抢占。这是论文未讨论的一个重要边界条件。

SlidingServe 退化或失效的场景:

  • 纯短 prompt 负载: 预填充一次完成,SlidingChunker 退化为单步调度,无额外收益。在此场景下,SlidingServe 的调度开销(预测器调用、三分搜索)是纯成本,没有任何吞吐量收益。论文未提供调度器 CPU 耗时的详细分解数据来量化这个开销。
  • 极重过载(QPS >> goodput): 所有请求几乎都处于违约风险,BatchConstructor 反复削减批次,系统整体吞吐量下降,违约率仍居高不下。
  • 多节点分布式推理(PP > 1): Pipeline 并行引入 bubble 开销,预测器特征不捕获 pipeline 气泡,调度决策可能失准。
  • 混合精度量化(INT4/INT8): 计算带宽特性改变,7 维特征的权重系数需要重新拟合。
  • 输出长度高度可变的任务: 公式(1)的逐 token 截止时间模型在输出长度极长(> 2000 token)时可能导致资源过度分配给接近尾部的 token,而非新到达的短请求。

10.5 未来方向与开放问题

SlidingServe 的设计为若干有趣的后续研究方向打开了大门:

与实时调度理论的联系。 SlidingServe 的问题与实时系统理论有深刻联系。TBT 截止时间模型(公式 1)本质上是一个周期性实时任务模型:每个请求以 LitbtL_i^{\text{tbt}} 为周期生成一个截止时间任务。经典实时调度理论中,EDF 对单处理器周期任务是最优的。LLM 服务与经典 RT 模型的关键差异:①请求非周期到达;②”处理器”(GPU 批次)的有效容量随批次组成变化;③任务执行时间不固定(批次内相互干扰)。这些偏差正是问题困难之处,也是 SlidingServe 的定制算法需要处理的核心。未来工作可以探索在简化假设下能否将 RT 调度理论的可证明保证移植到 LLM 服务场景。

方向一:超过 2 步的滑动窗口。 三分搜索框架可以自然推广到窗口大小 k>2k > 2:联合优化 b1,,bkb_1, \ldots, b_k 使得 bi=BΣ\sum b_i = B_\Sigmaf(bi)Tif(b_i) \leq T_i。这变为一个 kk 维约束优化问题,当 k4k \leq 4 时可以用坐标下降或梯度法高效求解。关键问题是额外的信息增益是否值得计算开销。

方向二:可学习的延迟预测器。 7 维线性预测器在固定硬件上工作良好,但跨 GPU 系列泛化能力弱。一个 2–3 层的小型 MLP(参数量 < 10K)能捕获特征间的交互,同时用 ONNX runtime 保持推理延迟 < 10 μs。关键挑战是在线增量更新的稳定性。

方向三:支持请求抢占。 SlidingServe 当前不支持抢占(暂停运行中的请求、换出 KV 缓存到 CPU/磁盘)。将抢占决策集成进 DP 求解器,可以在”无法构造满足所有截止时间的批次”时,选择换出价值最低的请求而非允许违约,进一步降低违约率。

方向四:多模型共享 GPU 服务。 生产集群越来越多地在同一 GPU 上服务多个模型。MLPS 的紧迫度计算和 SlidingChunker 的窗口约束需要扩展,以考虑跨模型的资源竞争和模型切换开销。

方向五:与投机解码的集成。 投机解码(Speculative Decoding)让每次前向传播可以验证/生成多个 token,使 TBT 模型从”每步 1 token”升级为”每步平均 α>1\alpha > 1 token”。SlidingServe 的截止时间公式(式 1)需要相应修改:di,k+1=ai+Littft+k/αLitbtd_{i,k+1} = a_i + L_i^{\text{ttft}} + \lfloor k/\alpha \rfloor \cdot L_i^{\text{tbt}}(其中 α\alpha 是投机接受率)。

11. 总结

SlidingServe 在正确的问题上做了扎实的工程。它识别了 LLM 调度中长期被忽视的两个问题(跨迭代预算分配低效和批次内 TTFT 干扰),并给出了形式化、可实现的解决方案:滑动窗口联合优化和 0/1 背包批次选择。

最让我感到有价值的是 BatchConstructor 的设计哲学:不是简单地”加入/丢弃”请求,而是把批次构建问题形式化为一个带有业务语义的约束优化问题,价值函数同时编码了紧迫性和工作量,锚点机制确保了最需要保护的请求一定被服务。这种思路比纯启发式规则更健壮、更可解释,也更容易扩展到多租户、多 SLO 类型的场景。

论文最大的开放性问题是规模化:当前评估仅覆盖 7B 模型和两卡 TP2 配置,无法说明系统在生产级 A100/H100 集群、70B+ 模型或更复杂 SLO 结构下的表现。未来工作需要在这些维度上提供证据,才能真正确立 SlidingServe 在生产环境中的价值。

可复现性说明。 论文使用公开数据集(HuggingFace 上的 ShareGPT 和 arXiv-Summarization)和标准硬件(RTX 3090),实验设置透明。但截至投稿日,代码尚未开源。BatchConstructor 的 DP、SlidingChunker 的三分搜索和预测器训练流程均有算法层面的完整描述,具备复现条件,但缺少开源实现使独立验证存在较高门槛。

从学术贡献的角度看,SlidingServe 的价值在于:将 LLM 调度中两个长期被当作”工程直觉”处理的问题(跨迭代预算分配、批次内请求选择)升级为有数学形式的优化问题,并给出了实用的近似算法(三分搜索 + DP 背包)。从研究方法论的角度看,SlidingServe 体现了系统设计中”形式化-再求解”的范式:将非正式观察(贪婪局部最优 ≠ 全局最优)形式化为数学目标(式 9, 19),再提供复杂度明确的高效算法(三分搜索 + DP 背包)。这比纯启发式规则更具可分析性、可扩展性。

这种”形式化 → 近似求解 → 集成验证”的研究范式值得在更多 LLM 系统问题上推广——例如 KV 缓存预取的时机决策、投机解码的 draft 长度选择,以及多租户资源隔离等问题都可以用类似框架来处理。

四个组件的创新度再评估:

  • 批次延迟预测器: 用线性回归做系统延迟预测并不新颖,但针对 LLM 批次设计的 7 维特征向量具有明确的物理动机和很好的实用效果。
  • 多级优先级排序器: 将现有启发式(紧迫度 + 公平性 + 工作量)形式化为字典序优先级键,使调度器的行为可量化、可调整。
  • SlidingChunker: 最具新颖性的贡献。将块大小选择形式化为两步联合优化并用三分搜索求解,是对现有单步贪婪策略的具体算法进步。
  • BatchConstructor: 将批次内请求选择形式化为 0/1 背包问题,识别了 LLM 服务领域此前未被注意的问题并给出了形式化解法。

四个组件合在一起构成了一个设计一致、方法论严谨的系统。在更广泛的硬件和模型规模上验证这些结论,是未来工作最重要的任务。

对 LLM 基础设施生态的更广泛影响。 SlidingServe 的”预测器驱动调度”模式创造了一种新的设计范式:收集硬件剖析数据,训练轻量模型,用该模型做本来需要昂贵仿真或启发式近似的调度决策。这个模式具有通用性:同样的思路可以用于入队控制(预测新请求是否能被接受而不违反现有 SLO)、KV 缓存预取(预测未来 2 轮迭代需要哪些 KV 页),或抢占决策(预测驱逐哪个请求对 Goodput 影响最小)。

个人读后感。 SlidingServe 是一篇问题清晰、算法有据、实验可信的系统论文。选择精确 DP 而非近似、选择三分搜索而非梯度方法,体现了作者对问题规模的成熟判断。主要短板是评估范围过窄。对于实际部署 LLM 服务的工程师,SlidingChunker 的两步联合优化思路和 BatchConstructor 的批次内 DP 筛选思路均有直接的工程借鉴价值,值得在自己的系统上尝试实现——即使不全量引入 SlidingServe 框架,这两个核心思路单独集成也可能带来可观的 SLO 改善。


笔记人:Zhongzhu Zhou,2026-06-07。论文:arXiv:2606.05933v1。作者测试硬件:2× NVIDIA RTX 3090 (TP2)。测试模型:Llama3-8B、Qwen2.5-7B。基线对比:Sarathi-EDF、QoServe (ASPLOS 2026)。数据集:ShareGPT、arXiv-Summarization-v1/v2、mixed-v1、mixed-v2。