Orca: The beginning of Morden LLM serving.
Orca: The beginning of Morden LLM serving.
总的来说,ORCA最重要的历史贡献,是在Transformer自回归推理中系统化提出Iteration levelscheduling, 迭代级调度和Selective Batching(选择性批处理), 前者是后来通常所说的Continuos batching的重要基础。
Orca改变的是推理引擎最基本的执行单位,这项设计今天仍然适用于单卡和双卡推理。
Abstract
论文的摘要很直接。 首先,传统的Request level batching不适合自回归生成。例如现在我们有三个请求
- A 需要生成100 tokens
- B 需要 20 tokens
- C 在A \ B 执行10轮后到达。
2022年那会,传统的Request Batching会把A \ B 作为固定Batch交给Execution Engine,等B返回之后,还得等待A运行,而C则要等待一整个Batch才能加入。
所以Orca自然不会放过这个现象,Orca让Scheduler在每次model iteration结束之后,重新决定batch的组成。
| Iterations | 1 | 2 | 3 | 4 | 5 |
|---|---|---|---|---|---|
| Traditional | A+B | A+B | A+B | A+B | C |
| ORCA | A+B | A+B | A+C | A+C | A+C |
这样batch就不再是固定的了
而随之而来的一个问题就是,不同请求的Sequence Length 不一样,怎么Batch呢
所以这就是第二个创新点的来源, Selective Batching,只对Transformer中适合批处理的算子执行batching,而不要求所有算子都采用相同的batch结构
例如现在有三个亲求,两个是Decode,一个是batching
那么对于Decode的来说,一般的Tensor shape是 1x d 的Q, 和 context lenght x d 的 k
而对于prefill,就是token x d
这些attention操作无法直接组成一个具有统一序列长度的普通Dense batch,如果为了统一形状,要padding的话就会产生大量的无效计算
所以作者认为,不必要强制所有算子一起batch,这就是selective batching,一个Transformer block中,有些算子不关心输入来自哪条请求,例如Linear,只要hidden size相同就可以
Introduction
这一部分,我们重点看看作者对Attention的解释。
在计算注意力的时候,我们一般是
Q = XW_q K = XW_k V = XW_v
这些Linear PROJECTION 是有模型参数的,而GPU Decode的时候,反复从HBM中加载模型权重是重要成本。增大Batch size就可以提高权重的复用效率
作者宁愿让ATTENTION 分别执行,也要让Linear / MLP 能够以更大的batch执行,因为后者通常能够获得更明显的权重方寸摊销收益。
Chanllenges and Proposed Solutions
作者提出了serving里面的两个困难
- Early-finished / Late- joining Requests
- Batching an Arbitrary Set of Requests
就是request的到达不同、request的并行需求不同
Orca的基本结构非常简单:
他们通过维护一个基础的 Request pool ,通过scheduler选入到 Scheduler中进行协调。然后交给Execution Engine.
与传统的Serving 区别是,engine只负责执行一次Iteration,完成之后就把控制权交还给Scheduelr。
假设Request pool中有
- A: 已经生成20 tokens, going on
- B: 5tokens , going on
- C just arrived
然后scheduler可以根据输入来构造
这个东西其实就是线代的推理引擎中 scheduler state Management 的雏形。
Selective batching: 此处,假设有 (1,1,2,3) 四种token数目的亲求,作者不是构造
Distributed Architecture
作者把控制信息 (Request IDs, Token Indices) 和实际的GPU Tensor Data分开传输
- Control Plane 通过独立通信通道传输调度元数据,避免务必要的GPU同步
- Data Plane: 通过NCCL 传输真正的Tensor data
目的就是让GPU持续运行。不用等待CPU通过GPU通信机制同步控制信息。
Scheduling Algorithm
1.调度原则: Iteration - level First Come First Serve
假设请求A比B更早,那么A的Iteration数目不应该少于B
2. KV Cache Reservation
作者定义
- max_bs: 最大 Batch size
- n_slots: 可用的KV Cache Token slots 总数
- n_rsrv 目前预留的 Token slots
- max_tokens 某条请求允许达到的最大的总token数目,包含输入和生成部分
下面是核心代码:
1 | def select_batch(requests, reserved, capacity, max_bs): |
假设GPU有2k个KV slots,A用 800, B用 700, C到达之后如果预留不够,也会拒绝C
3. Pipeline
Orca 允许多个 Batch 同时处于不同的 pipeline stages
如果有三个 workers,它不必让所有的workers 一起执行 batch A , 然后在开始B,可以让不同的Batch自己来运行,作者通过控制 In flight batch, 让pipeline尽量busy
Evalutaion
作者首先关闭ORCA的动态调度,让所有的请求具有相同的输入长度和生成长度,使用固定的batch执行。
相当于是和FastTransformer直接对打。
最后的结果让人惊讶,小模型上面反而更慢,由于 Orca 不使用传统方式统一 Batch Attention,执行效率有一定损失。
不过对于175B的大模型,ORCA就有优势了。作者归功于Control Plane, Data Plane.
加入Scheduler之后,优势就大得多了。
在16x A100 , GPT3 - 175B的情况下, orca相比于FT达到了36.9x的加速。
我的评价。
首先呢,我在读这篇文章的时候其实以恶个很直接的想法是: 这个东西难道之前不是这样的吗?
不过这篇文章是2022年的,也是很早之前的了,所以AI的发展真的是日新月异啊。
在当时,那会完成serving请求的时候,是通过一个一个普通的请求放到一起,然后batch,甚至还没有什么调度器出现。
而这篇文章也就是做了很显然的一个事情嘛,把调度器加进来,让它尽量减少bubble,因为这个地方就是具有长尾的现象的。这就是一个十分简单的观察点。
所以我们在做科研的时候,我觉得很有可能我们要做的创新点不能求那么大,就是把一个微笑的工作做好做出来就行了。
至于后面怎么提升,怎么变好,那就慢慢去打磨吧。







