跳到主要内容
画开 vLLM:那个把 prefill 和 decode 炒在一起的调度器

画开 vLLM:那个把 prefill 和 decode 炒在一起的调度器

画唠
画唠

· 阅读约 2 分钟

vLLM 的 V1 引擎最近被讲得挺玄。尤其是那个调度器,我之前绕了挺久,因为 V0 留下的刻板印象太深了。

我以前一直以为,推理嘛,一个 step 就是一条线走到底。先做 prefill,等 prompt 处理完吐出第一个 token,再进 decode 阶段一个一个往外蹦。画成图大概是这样:

   一个 step 的任务:
   [.prefill.....] ──等完──► [decode] [decode] [decode] ...

一条流水线,各管各的。V0 基本上就是这么干的。

但前两天我去翻了 Aleksa Gordic 那篇拆 vLLM V1 源码的文章,对着调度循环的代码看,第一版我画错了。V1 的调度器根本不这么排队。

重来。V1 的调度器能在一个 step 里把 prefill 和 decode 请求混排在一起。画出来其实是这样的:

   一个 step:
   ┌─────────────────────────────────────────────┐
   │   decode 请求们(已经在跑的)    │  prefill(新来的)│
   │   [d] [d] [d] [d] [d] [d] [d]   │   [prefill块]    │
   └─────────────────────────────────────────────┘

它不是一个接一个,而是把已经在生成 token 的 decode 请求打包,再塞个刚进来的新 prefill 请求一起算。混排。

我盯着这块看了半天,这玩意儿不卡死吗?新来的长 prompt 做 prefill 不是得占算力吗?

原来如此。调度器在这里面有个绝对优先级:正在 running 队列里的 decode 请求是大爷,得先紧着它们喂。新来的 prefill 请求哪怕排着队,也不能把正在吐字的请求给卡住。

而且如果 prompt 太长,怕它一个人霸占整个 step 怎么办?V1 有个机制:直接把长 prompt 切成小块,切成那种不会一口吞掉全局算力的颗粒度。

打个比方:这就像饭店后厨。V0 是炒完一桌大菜(prefill)再去顾散客的单(decode)。V1 是锅子全开,炒着散客的快菜(优先 decode),顺手把新来的大菜切一切(分块 prefill)扔进同一个锅里一起炖。

这个比喻在这里漏风了:真实厨房可能串味,但 GPU 的计算资源是可以通过 roofline 模型切分复用的。只要 batch 没饱和,把 prefill 塞进 decode 的间隙,吞吐量就能往上拉。

懂了的那一刻,真的咔哒一下扣上了。💡 把这两个阶段混排在一个 step 里,不搞一刀切,吞吐量和延迟的坑一下全填平了。这玩意儿真的太酷了。

下期想画开哪个概念?我想去拆那个用有限状态机做 guided decoding 的逻辑,你们觉得呢?

画唠
画唠

把被讲玄的概念用图 + 比喻 + 动手实验拆到咔哒扣明白,错的也保留。

查看主页 →

更多「效率」的实战

评论(1)

老张老张

分块prefill的粒度是按token数还是按roofline动态算的?