阅读论文5——ORCA: A Distributed Serving System for Transformer-Based Generative Models
ORCA: A Distributed Serving System for Transformer-Based Generative Models
-
摘要
-
Introduction
-
Background
-
- GPT推理程序
- ML 推理服务系统
-
Challenges and Proposed Solutions
-
- 对于提前完成和延迟加入的请求在请求级面临的问题
- 对于提前完成和延迟加入的请求进行处理迭代级调度
- 批量处理任意一组请求面临的问题
- 批量处理任意一组请求 选择性批处理 Selective batching
-
ORCA Design
-
- 分布式架构
- 调度算法
-
- 流水线并行
-
Implementation
-
Evaluation
-
- 环境
- 模型
- 基准系统
- 情景
- 对于第一个场景进行对比:Engine Microbenchmark
- 对于第二个场景进行对比:端到端的性能
-
- 不同的批量大小配置
- 同构请求的跟踪
-
Related Work and Discussion
-
- 递归模型的细粒度批处理
- Transformer模型的专用执行引擎
- 服务系统与执行引擎之间的接口
-
总结
本篇要看的论文是关于大模型推理的另一项技术Continuous Batching,还有一个技术是PagedAttention,上篇论文已经介绍过了
关于Continuous Batching技术,它最早原为ORCA系统,论文是《ORCA: A Distributed Serving System for Transformer-Based Generative Models》
摘要
最近,为生成任务训练的基于Transformer的大规模模型(如 GPT-3)引起了人们的极大兴趣,强调了为该系列模型提供系统支持的必要性。由于这些模型以自回归的方式生成下一个标记,因此需要多次运行模型来处理推理请求,而模型的每次迭代都会为请求生成一个输出标记。然而,现有的推理服务系统在处理这类具有多迭代特征的工作负载时表现不佳,原因是它们的调度机制不够灵活,无法改变当前正在处理的请求批次;比批次中其他请求更早完成的请求无法返回客户端,而新到达的请求则必须等到当前批次完全处理完毕。
在本文中,我们提出了迭代级调度(iteration-level scheduling),这是一种新的调度机制,它以迭代(而非请求)为粒度调度执行,调度器只调用执行引擎在批次上运行模型的单次迭代。此外,为了同时对 Transformer 模型应用批处理和迭代级调度,我们建议采用选择性批处理,即只对选定的操作集应用批处理。在这两种技术的基础上,我们实现了一个名为 ORCA 的分布式服务系统,并进行了额外的设计,使其可扩展至拥有千亿级参数的模型。我们在 GPT-3 175B 模型上进行的评估表明,ORCA 在延迟和吞吐量方面都明显优于英伟达™ FasterTransformer:在延迟相同的情况下,吞吐量提高了 36:9 倍。
Introduction
生成模型的核心是Transformer架构和在原始Transformer架构进行改进或优化的各种版本。通过依赖注意机制[60],Transformer模型可以学习到更好的表示,其中序列的每个元素可能与其他元素有直接连接,这在循环模型中实现不了。
语言生成任务对于聊天机器人 [9,52]、摘要 [41,45,54]、代码生成 [13] 和标题生成 [65,66] 等许多类型的应用来说越来越重要。此外,AI21 实验室[37]、DeepMind [26,48]、谷歌[15,21,63]、Meta Platforms [10,67]、微软[50]、微软和英伟达[59]以及 OpenAI [12]最近发表的研究报告指出,包括翻译[11,17]、分类[20,53]、问题解答[32,33,40]等在内的每一种语言处理任务都可以被视为语言生成问题,并在这一方向上取得了巨大进步。生成模型的兴起并不局限于语言领域,人工智能界对其他领域(如图像、视频、语音或多个领域的混合)的生成问题也越来越感兴趣[19,38,51,62]。生成模型的核心是 Transformer 架构[60]及其变体[15, 47-49]。依靠注意力机制[60],Transformer 模型可以学习到更好的表征,序列中的每个元素都可能与其他元素有直接联系,而这在递归模型中是不可能实现的[25]。
为了在实际应用中使用生成模型,将推理过程委托给负责机器学习推理服务的单独服务,这个使用不是合并的,而是被拆分出来的服务系统单独服务。
使用Triton和FasterTransformer的组合来部署语言生成任务的服务,Triton是服务系统,FasterTransformer是执行引擎。其中Triton主要负责将多个客户端请求分组为批处理,而FasterTransformer接收来自Triton的批处理并以批处理的方式进行推理过程。
为了在实际应用中使用生成模型,我们经常将推理过程委托给负责ML推理服务的单独服务。该服务应该以低延迟和高吞吐量为客户端请求提供推理结果,对该服务的需求不断增长,促进了推理服务系统的发展,如Triton inference Server[7]和TensorFlow serving[42]。这些系统可以使用单独开发的DNN执行引擎来执行实际的张量操作。例如,我们可以通过使用Triton和FasterTransformer[4]的组合来部署语言生成任务的服务,FasterTransformer是一种针对基于transformer的模型的推理进行优化的执行引擎。在这种情况下,Triton主要负责将多个客户端请求分组为批处理,而FasterTransformer接收来自Triton的批处理并以批处理的方式进行推理过程。
现有的推理系统含有服务系统层和执行引擎层这两层,但这两层在处理基于transformer的生成模型的请求方面存在局限性。这种局限性其实是源于transformer的自回归
对于transformer而言,一个请求运行模型的次数与要生成的令牌数量一样,而对于ResNet和BERT等其他模型,一个请求只需要通过运行模型一次。
不幸的是,我们注意到现有的推理系统,包括服务系统层和执行引擎层,在处理基于transformer的生成模型的请求方面存在局限性。由于这些模型被训练成以自回归的方式生成下一个令牌,因此应该运行模型的次数与要生成的令牌数量一样多,而对于ResNet[24]和BERT[18]等其他模型,一个请求可以通过运行模型一次来处理。也就是说,为了处理对生成模型的请求,我们必须运行模型的多次迭代;每次迭代都会生成一个输出令牌,在接下来的迭代中用作输入。这种多迭代特性对当前推理系统的设计提出了质疑,其中服务系统按请求粒度调度引擎的执行。在这种设计下,当服务系统向引擎发送一批请求时,引擎在处理完批内的所有请求后,立即返回整个批的推理结果。由于不同的客户端请求可能需要不同的处理迭代次数,因此在批处理中比其他请求早完成的请求无法返回到客户端,从而导致延迟增加。在调度批处理之后到达的请求也应该等待处理批处理,这会显著增加请求的排队时间。
在本文中,我们建议以迭代粒度来代替请求粒度来调度引擎的执行。特别是,服务系统调用引擎只在批处理上运行模型的单个迭代。因此,在等待模型的一次迭代之后,可以考虑处理新到达的请求。服务系统在每次从引擎返回后检查请求是否已经完成处理-因此完成的请求也可以立即返回给客户端。
然而,当我们试图同时应用批处理和迭代级调度时,就会遇到一个明显的挑战。与典型的请求级调度不同,提出的调度可以发出一批请求,其中每个请求到目前为止已处理了不同数量的tokens。在这种情况下,向transformer模型发出的请求无法以批量方式进行处理,因为注意力机制要求进行不可批处理的张量运算,其输入张量的形状会根据已处理token的数量而变化。
为了应对这一挑战,我们建议只对一组选定的操作应用批处理,我们称之为选择性批处理。通过考虑操作的不同特征,选择性批处理将拆分批处理,并在对Transformer模型的其他操作应用批处理的同时,为Attention操作单独处理每个请求。我们观察到,不批量执行注意操作的决定对效率只有很小的影响。由于Attention操作不与任何模型参数相关联,因此对Attention应用批处理并没有通过跨多个请求重用加载的参数来减少GPU内存读取量的好处。
基于这些技术,我们设计并实现了基于变压器生成模型的分布式服务系统ORCA。为了处理大规模模型,orca采用了层内和层间模型并行化策略,这些策略最初是由训练系统为Transformer模型开发的[55,58]。我们还为提议的迭代级调度设计了一个新的调度算法,并考虑了内存管理和跨工作线程的流水线执行。
我们使用具有各种配置的OpenAI GPT-3[12]模型评估ORCA,扩展到341B个参数。结果表明,ORCA显著优于FasterTransformer[4],在相同的延迟水平下,其吞吐量提高了36.9倍。虽然我们在整篇论文中使用语言模型作为驱动示例,并且只在语言模型上进行实验,但只要模型基于Transformer架构并使用自回归生成过程,其他领域的生成模型就可以从我们的方法中受益[19,38,51,62]。
能够了解到,ORCA采用了迭代级的调度、选择性批处理或连续批处理技术,设计了一个新的调度算法,并考虑了内存管理和跨工作线程的流水线执行,采用了层内和层间模型并行化策略.
Background
我们提供了GPT推理过程的背景[12,47],这是我们在本文中使用的基于transformer的生成模型的代表性示例,以及ML推理服务系统。
GPT推理程序
GPT是一种基于Transformer的一种架构变体的自回归语言模型[60]。它将文本作为输入,并产生新的文本作为输出。具体而言,模型接收一系列输入令牌,然后通过生成后续输出令牌来完成该序列。图1a展示了一个简化的计算图,它用三层GPT模型表示这个过程,其中节点和边分别表示Transformer层和层之间的依赖关系。Transformer层按照节点上的数字表示的顺序执行,使用相同模型参数集的节点(即表示相同层的节点)用相同的颜色填充。
生成的输出令牌被反馈到模型中以生成下一个输出令牌,从而实施一个顺序的、逐个的推理过程。生成单个令牌的这个自回归过程是通过使用输入运行模型的所有层来完成的,输入要么是来自客户机的输入令牌序列,要么是以前生成的输出令牌。我们将所有层的运行定义为模型的迭代。在图1a所示的示例中,推理过程包含三个迭代。第一次迭代(“iter 1”)一次接受所有输入标记(“I think this”)并生成下一个标记(“is”)。此迭代包含一个初始化阶段,该阶段负责处理输入令牌并生成第一个输出令牌。接下来的两个迭代(“iter 2”和“iter 3”)组成一个增量阶段,使用前一个迭代的输出标记并生成下一个标记。在本例中,“iter 3”是最后一次迭代,因为它产生“< EOS>”,这是一个特殊的序列结束标记,用于终止输出生成。请注意,虽然增量阶段包含多个迭代,因为每个迭代只能处理单个令牌,但初始阶段通常通过并行处理所有输入令牌来实现为单个迭代.

原始的Transformer[60]采用了两个Transformer层堆栈,而GPT的体系结构由一个单层堆栈组成,即解码器。图1b显示了GPT中使用的Transformer层。在组成Transformer层的操作中,Attention是区分Transformer与其他体系结构的本质。在较高的级别上,Attention操作计算感兴趣的令牌的加权平均值,以便序列中的每个令牌都知道其他令牌。它接受三个输入:查询、键和值,计算查询(对于当前令牌)与所有键(对于感兴趣的令牌)的点积,对点积应用Softmax以获得权重,并对与权重相关的所有值进行加权平均。
解释。一个naïve无状态推理过程将获取序列中的所有令牌(包括客户端提供的输入令牌和到目前为止生成的输出令牌),以便在每次迭代时重新计算所有键和值。
在计算机科学和人工智能领域,一个系统如果被称为“无状态”的,那么它就不会记住或保留之前处理的数据或事件的状态。每次处理新的输入时,它都会从零开始,不考虑之前的任何上下文或历史。
一个naïve(朴素的)无状态推理过程在处理序列令牌(tokens)时的行为:
- 获取所有令牌:这个过程会收集序列中的所有令牌,这些令牌包括客户端最初提供的输入令牌以及系统到目前为止生成的任何输出令牌。这意味着,无论当前迭代是在处理序列的开始还是结束,系统都会重新获取整个令牌序列。
- 重新计算所有键和值:由于是无状态推理,系统在每次迭代时不会利用之前计算的结果。相反,它会基于当前获取的所有令牌重新计算所有可能的键和值。这种方法的效率非常低,因为很多计算可能是重复的,尤其是在序列较长的情况下。
- 迭代处理:这个过程会不断重复上述步骤,每次迭代都从头开始处理整个序列,直到达到某个终止条件(例如,生成了特定数量的输出令牌,或者达到了某种计算上的限制)。
由于Attention需要前面所有令牌的键和值,我们将键和值视为应该在多个迭代中维护的内部状态。一个naïve无状态推理过程将获取序列中的所有令牌(包括客户端提供的输入令牌和到目前为止生成的输出令牌),以便在每次迭代时重新计算所有键和值。为了避免这种重新计算,fairseq[43]建议增量解码,这样可以保存密钥和值以便在连续迭代中重用。其他Transformer系统,如FasterTransformer[4]和Megatron-LM[3]也做同样的事情。
图 1c 展示了 Transformer 和同样保持内部状态的 LSTM [25]的状态使用模式。主要区别在于,Transformer 的状态大小(k 表示注意力键,v 表示值)会随着迭代而增加,而 LSTM 的状态大小(c 表示 LSTM 内部存储器,h 表示 LSTM 层的输入/输出)则保持不变。在处理索引 t 处的标记时,Attention 操作会将之前的所有 Attention 键 kl;1:t-1和值 vl;1:t-1 与当前键 kl;t 和值 vl;t 一并处理。因此,“注意 ”操作应根据已处理标记的数量对不同形状的张量进行计算。
在 “注意 ”操作之前,需要进行层规范化操作(LayerNorm)和 QKV 线性操作(线性和拆分操作,以获取查询、键和值)。在 “注意 ”操作之后执行的操作依次是线性操作(Attn Out Linear)、残差连接添加操作(Add)、层规范化操作(LayerNorm)、多层感知器(MLP)操作和其他残差连接操作(Add)。
ML 推理服务系统
对 ML 驱动型应用的需求日益增长,使得 ML 推断服务成为现代数据中心的重要工作负载。用户(终端用户或应用程序的内部微服务)向推理服务提交请求,而服务则根据预先定义的 ML 模型,利用其提供的资源(通常配备 GPU 和 TPU 等专用加速器)对请求做出回复。具体来说,该服务使用输入数据运行 DNN 模型,为请求生成输出。就像在数据中心运行的其他服务一样,管理良好的推理服务应在合理的成本范围内提供低延迟和高吞吐量。
为了满足这些限制,服务运营商通常会使用 ML 推断服务系统,如 Triton Inference Server [7] 和 TensorFlow Serving [42]。这些系统可被视为底层模型执行引擎(如 TensorRT [6]、TVM [14]、TensorFlow [8],以及其他许多引擎 [44,46])之上的抽象概念,与各种 ML 模型、执行引擎和计算硬件无关。服务系统在将驱动主要数学运算的角色委托给引擎的同时,还负责暴露接收推理请求的端点、调度引擎的执行以及发送对请求的响应。因此,这些系统关注的方面包括批处理执行[7, 16, 35, 42, 56]、从多个模型变体中选择合适的模型[16,27,30,57]、在同一设备上部署多个模型(每个模型用于不同的推理服务)[7, 29, 35, 56]等。因此,这些系统关注的方面包括批处理执行[7, 16, 35, 42, 56]、从多个模型变体中选择合适的模型[16,27,30,57]、在同一设备上部署多个模型(每个模型用于不同的推理服务)[7, 29, 35, 56]等。
在服务系统提供的功能和优化中,批处理是使用 GPU 等加速器时实现加速器高利用率的关键。当我们启用批处理功能运行执行引擎时,来自多个请求的输入张量会聚合成一个大的输入张量,然后再送入模型的第一个操作。由于加速器更喜欢大输入张量而不是小输入张量,以便更好地利用大量并行计算单元,因此引擎的吞吐量在很大程度上取决于批量大小,即引擎一起处理的推理请求数量。重复使用从片外内存加载的模型参数是批处理执行的另一个优点,尤其是当模型涉及内存密集型操作时。

图2显示了使用现有服务系统和执行引擎服务生成语言模型的总体工作流。服务系统(例如,Triton[7])的主要组件是调度程序,①负责通过从队列中检索请求来创建一批请求,②调度执行引擎(例如,FasterTransformer[4])来处理该批请求。③执行引擎通过运行被服务的模型的多次迭代来处理接收到的批处理,④将生成的文本返回给服务系统。在图2中,服务系统调度引擎分批处理两个请求(x1:“I think”,x2:“I love”),引擎对请求x1和x2分别生成“this is great”和“you”。
Challenges and Proposed Solutions
在本节中,我们描述了服务基于transformer的生成模型所面临的挑战,并提出了两种技术:迭代级调度和选择性批处理。
对于提前完成和延迟加入的请求在请求级面临的问题
现有系统的一个主要局限是,服务系统和执行引擎只有在以下情况下才会相互影响:(1) 服务系统在空闲引擎上调度下一个批次;或 (2) 引擎完成当前批次的处理。换句话说,这些系统被设计为以请求粒度来调度执行;引擎会固定维护一批请求,直到该批请求全部处理完毕。这在为生成模型提供服务时可能会出现问题,因为批次中的每个请求可能需要不同的迭代次数,从而导致某些请求比其他请求更早完成。在图 3 所示的示例中,虽然请求 x2 比请求 x1 完成得早,但引擎在所有迭代过程中都会对 “活动 ”和 “非活动 ”请求进行计算。这种对非活动请求的额外计算(迭代 3 和 4 中的 x2)限制了分批执行的效率。

更糟糕的是,这种行为会阻止将处理完毕的请求尽早返回客户端,从而造成大量的额外延迟。这是因为引擎只有在处理完批次中的所有请求后,才会将执行结果返回给服务系统。同样,当一个新请求在当前批次的执行过程中到达时,上述调度机制会让新到达的请求等待,直到当前批次中的所有请求都处理完毕。我们认为,当前的请求级调度机制无法有效处理具有多迭代特性的工作负载。需要注意的是,在语言模型的训练中不会出现这种请求提前完成和延迟加入的问题;训练程序通过使用教师强制技术(teacher forcing technique)[64],在一次迭代中完成对整个批次的处理。
对于提前完成和延迟加入的请求进行处理迭代级调度
为解决上述限制,我们建议在迭代粒度上调度执行。在高层,调度程序会重复以下步骤:(1) 选择下一个要运行的请求;(2) 调用引擎对所选请求执行一次迭代;(3) 接收预定迭代的执行结果。由于调度程序每次迭代都会收到返回结果,因此它可以检测到请求的完成情况,并立即将生成的令牌返回给客户端。对于新到达的请求,请求有机会在当前计划的迭代执行后开始处理(即调度器可能会选择下一个运行的新请求),从而大大减少了排队延迟。有了迭代级调度,调度员就能完全控制每次迭代处理多少个请求和哪些请求。

图 4 描述了 ORCA 使用迭代级调度的系统架构和整体工作流程。ORCA 公开了一个端点(如 HTTPS 或 gRPC),推理请求通过该端点到达系统,而对请求的响应则通过该端点发送。 端点将新到达的请求放入请求池,请求池是一个在请求生命周期内管理系统中所有请求的组件。请求池由调度器监控,调度器负责:从请求池中选择一组请求,调度执行引擎在这组请求上运行模型迭代,从引擎接收执行结果(即输出标记),并通过将每个输出标记附加到相应请求来更新请求池。引擎是执行实际张量运算的抽象概念,可在多台机器的多个 GPU 上并行执行。在图 4 所示的示例中,①与请求池交互,决定下一步运行哪些请求,② 调用引擎运行四个选定的请求:(x1; x2; x3; x4)。调度程序向引擎提供首次调度的请求的输入标记。在这种情况下,x3 和 x4 还没有运行任何迭代,因此调度程序为 x3 发送了 (x31; x32),为 x4 发送了 (x41; x42; x43)。③ 引擎会对这四个请求运行一次模型迭代,④ 返回生成的输出令牌(x15; x23; x33; x44),每个调度请求一个令牌。一旦请求处理完毕,请求池就会删除已处理完毕的请求,并通知端点发送响应。与图 2 所示的方法不同,ORCA 的调度程序可以在每次迭代时更改要处理的请求。我们将在后文中详细介绍如何在每次迭代中选择请求的算法。
批量处理任意一组请求面临的问题
当我们尝试在实践中使用迭代级调度时,我们将面临的一个主要挑战就是批处理。为了实现高效率,执行引擎应能以批处理的方式处理任意一组选定的请求。如果不进行分批处理,就必须逐个处理每个选定的请求,这样就会失去 GPU 的大规模并行计算能力。
介绍不能合并在同一个batch的情况三种
遗憾的是,即使是一对请求(xi ; xj),也不能保证在下一次迭代中,它们的执行可以合并并替换为批处理版本。有三种情况下,一对请求的下一次迭代无法合并在一起:(1)两个请求都处于启动阶段,且各自的输入令牌数量不同(如图 4 中的 x3 和 x4);(2)两个请求都处于递增阶段,且各自处理的令牌索引不同(x1 和 x2);或(3)每个请求都处于不同的阶段:启动阶段或递增阶段(x1 和 x3)。回顾一下,为了批量执行多个请求,每个请求的执行都必须由相同的操作组成,每个操作都消耗形状相同的输入张量。在第一种情况下,两个请求不能被批量处理,因为它们的输入张量的 “长度 ”维度(即输入标记的数量)不相等。如图 1c 所示,第二种情况下的请求在 Attention keys 和 Attention values 的张量形状上存在差异,因为每个请求处理的标记索引不同。对于第三种情况,我们不能对不同阶段的迭代进行批处理,因为它们的输入令牌数量不同;启动阶段的迭代并行处理所有输入令牌以提高效率,而在增量阶段,每次迭代只处理一个令牌作为其输入(我们假设使用 fairseq 式增量解码 [43])。
只有当所选的两个请求处于同一阶段、具有相同数量的输入令牌(在启动阶段)或具有相同的令牌索引(在递增阶段)时,批处理才会适用。这一限制大大降低了在实际工作负载中出现批处理的可能性,因为调度程序应希望同时出现两个符合批处理条件的请求。随着批处理规模的增大,这种可能性会以指数形式进一步降低,因此使用大批处理规模来提高吞吐量而不影响延迟是不切实际的。
批量处理任意一组请求 选择性批处理 Selective batching
选择性批处理高度灵活地将请求组成批处理。总结一下选择性批处理相干的事情:
- 有选择地只对少数运算应用批处理
选择性批处理意识到了每个操作的不同特性;它将批处理分割开来,对注意力操作的每个请求进行单独处理,同时对其他没有请求概念的操作应用token 批处理(而不是请求批处理)- 与这种不规则形状的张量不兼容的操作对张量进行扁平化处理
- 非注意矩阵乘法和层归一化等运算不需要扁平化处理
我们提出了选择性批处理技术,这是一种批处理执行技术,可以高度灵活地将请求组成批处理。这种技术不是通过 “批量化 ”组成模型的所有张量运算来处理批量请求,而是有选择地只对少数运算应用批处理。
上述有关批处理的主要问题是,上述三种情况对应的是形状不规则的输入(或状态)张量,无法将其凝聚成一个大张量并输入批处理操作。在典型的批处理机制中,每次迭代时,变换器层都会接收一个三维输入张量,其形状为[B;L;H],由多个[L;H]输入张量的请求批量串联生成,其中 B 为批量大小,L 为一起处理的令牌数量,H 为模型的隐藏大小。例如,在图 3 中,“迭代 1”(启动阶段)使用形状为 [2;2;H] 的输入张量,而 “迭代 2”(递增阶段)使用形状为 [2;1;H] 的张量。然而,当调度程序决定对图 4 中的批次(x1; x2; x3; x4)进行迭代时,启动阶段请求的输入(x3 : [2;H] 和 x4 : [3;H])无法合并成一个形状为 [B;L;H] 的张量,因为 x3 和 x4 的输入标记数不同,分别为 2 和 3。
有趣的是,并非所有操作都与这种不规则形状的张量不兼容。通过对张量进行扁平化处理,非注意矩阵乘法和层归一化等运算都可以在不规则形状的张量上运行。例如,上述 x3 和 x4 的输入张量可以组成一个形状为 [∑L;H] = [5;H] 的二维张量,而不需要明确的批量维度。这个张量可以输入到所有非注意力操作中,包括线性、层规范、添加和 GeLU 操作,因为它们不需要区分不同请求的张量元素。另一方面,注意力操作需要一个请求概念(即需要批量维度),以便只在同一请求的标记之间计算注意力,这通常是通过应用 cuBLAS 例程进行批量矩阵乘法来实现的。
选择性批处理意识到了每个操作的不同特性;它将批处理分割开来,对注意力操作的每个请求进行单独处理,同时对其他没有请求概念的操作应用令牌批处理(而不是请求批处理)。图 5 展示了选择性批处理机制在处理图 4 中描述的一批请求(x1; x2; x3; x4)时的情况。该批次有 7 个输入标记需要处理,因此我们将输入张量的形状设为 [7;H],并应用非注意力操作。在 “注意 ”操作之前,我们会插入一个 “拆分 ”操作,并针对每个请求在拆分张量上分别运行 “注意 ”操作。通过合并(Merge)操作,将注意力操作的输出合并为形状为 [7;H] 的张量,从而为其他操作带来批处理功能。

为使增量阶段的请求能使用之前迭代处理的令牌的 Attention 密钥和值,ORCA 将生成的密钥和值保存在 Attention K/V 管理器中。该管理器为每个请求单独维护这些密钥和值,直到调度器明确要求删除某个请求的密钥和值,即请求处理完毕。增量阶段请求(x1 和 x2)的注意力操作会从管理器中获取之前令牌(x1 为 x11;x12;x13;x2 为 x21)的密钥和值,并从拆分操作中获取当前令牌的查询、密钥和值,以计算当前令牌与之前令牌之间的注意力。
ORCA Design
基于上述技术,我们设计并实现了 ORCA:基于变换器生成模型的分布式服务系统。在描述图 4 时,我们已经讨论了 ORCA 的系统组件和整体执行模型。在本节中,我们将回答剩余的问题,即如何构建一个高效的系统,以扩展到拥有千亿参数的大规模模型。我们还将介绍迭代级调度的调度算法,即如何在每次迭代时从请求池中选择一批请求。
分布式架构
最近的研究[12, 31]表明,扩展语言模型可以显著提高模型的质量。因此,系统支持服务如此庞大的语言模型变得越来越重要,特别是当模型不适合单个GPU时。在这种情况下,应该拆分模型参数以及相应的计算,并将它们分布在多个gpu和机器上。
ORCA为Transformer模型组合了已知的并行化技术:层内并行化和层间并行化。FasterTransformer[4]也使用了这两种模型并行策略,它们最初是为分布式训练开发的。层内并行性[55,58]将矩阵乘法(即线性和注意力运算)及其相关参数拆分到多个gpu上。我们省略了该策略如何划分每个矩阵乘法的细节。另一方面,层间并行性在多个gpu上拆分Transformer层。ORCA为每个GPU分配相同数量的Transformer层。图6演示了一个4层GPT模型的层内和层间并行性的示例应用程序。将4层划分为2个层间分区,分区中的各层再细分为3个层内分区。我们将每个分区分配给一个GPU,总共使用6个GPU。

ORCA 执行引擎采用上述技术,支持分布式执行。图 7 描述了 ORCA 引擎的架构。每个工作进程负责模型的一个层间分区,可以放置在不同的机器上。具体来说,每个工作进程管理一个或多个 CPU 线程,每个线程专用于控制 GPU,线程数量取决于层内并行程度。

ORCA 执行引擎的执行程序如下。一旦引擎被安排对一批请求进行模型迭代,引擎主控程序就会将收到的有关已安排批次的信息转发给第一个工作进程(Worker1)。这些信息包括当前迭代的令牌和控制信息,控制信息由批次内请求的 ID、当前令牌索引(用于增量阶段的请求)和输入令牌数量(用于启动阶段的请求)组成。Worker1 的控制器会将从引擎主控器接收到的信息转交给 GPU 控制线程,每个线程会解析这些信息,并向其相关的 GPU 发布适当的 GPU 内核。例如,“注意 ”操作的内核使用请求 ID 和当前令牌索引来获取 “注意 ”K/V 管理器保存的先前键和值的 GPU 内存地址。与此同时,控制器还将控制消息转发给下一个 Worker(Worker2)的控制器,而无需等待 Worker1 的 GPU 上的内核完成。与 Worker1 不同的是,上一个 Worker(Worker2)的控制器会等待(即同步)已发布 GPU 内核的完成,以便获取每个请求的输出令牌,并将令牌发送回引擎主控器。
为了尽可能让 GPU 保持忙碌,我们在设计 ORCA 引擎时尽量减少 CPU 和 GPU 之间的同步。我们注意到,当前的分布式推理系统(如 FasterTransformer [4] 和 Megatron-LM [3])在每个进程接收控制信息时都会出现 CPU 与 GPU 同步,因为它们通过 GPU 与 GPU 之间的通信通道 NCCL [5] 交换信息。这些控制信息的交换在每次迭代中都会发生,带来不可忽略的性能开销。另一方面,ORCA 将控制信息(加上令牌)和张量数据传输的通信通道分开,避免了 CPU 使用 NCCL 传输数据。图 7 显示,ORCA 引擎只使用 NCCL 交换中间张量数据(虚线箭头表示),因为这些数据由 GPU 生成和消耗。CPU线程用于发布GPU内核的控制信息,通过单独的通信通道(如gRPC[2])在引擎主控和工作控制器之间发送,该通道不涉及GPU。
调度算法
ORCA 调度器在每次迭代时都会决定选择和处理哪些请求。 由于选择性批处理技术允许引擎以批处理方式运行任意一组请求,因此调度程序在选择一组请求组成批处理时具有很高的灵活性。现在剩下的主要问题是如何在每次迭代时选择请求 。
我们设计的 ORCA 调度器使用一种简单的算法,不会改变客户端请求的处理顺序;早到的请求会提前处理。也就是说,我们确保迭代级的先到先得(FCFS)特性。我们对具有多迭代特性的工作负载的迭代级 FCFS 特性定义如下:对于请求池中的任何一对请求(xi ; xj),如果 xi 比 x j 早到达,则 xi 应比 xj 运行相同或更多的迭代。需要注意的是,如果晚到的请求只需要较少的迭代次数就能完成,那么某些晚到的请求可能会更早返回客户端。
调度需要考虑增加bs造成的损失和gpu内存有限,针对这两个问题提出来解决办法
对于增加bs造成的损失,根据到达时间最多选择 “最大批量大小 ”的请求,最大批量大小提前人为设置好
对于gpu内存有限造成的,当调度程序无法对池中的任何请求由于没有剩余空间为下一个令牌存储新的注意力密钥和值而导致无法迭代,造成死锁。为首次调度的请求预留足够的空间来存储密钥和值
不过,调度程序还需要考虑其他因素:增加批处理量的收益递减和 GPU 内存限制。增加批量大小是以增加吞吐量来换取延迟的增加,但随着批量大小的增加,回报(即吞吐量的增加)也会减少。因此,与其他服务系统[7, 16]一样,ORCA 也有一个最大批量大小的概念:一个批量中可能存在的最大请求数。ORCA 系统操作员可以调整这个旋钮,在满足延迟预算的同时使吞吐量最大化。
另一个因素是 GPU 内存限制。通过在多个操作中重复使用中间结果的缓冲区 来优化内存使用是各种系统采用的著名技术[4, 6],ORCA 也采用了这一技术。不过,与可立即重用的中间结果缓冲区不同,Attention K/V 管理器用于存储键和值的缓冲区在 ORCA 调度器通知相应请求已处理完毕之前无法回收。当调度程序无法对池中的任何请求发出迭代时,由于没有剩余空间为下一个令牌存储新的注意力密钥和值,简单的实施会使调度程序陷入死锁。这就要求 ORCA 调度器了解管理器预分配内存区域的剩余大小。
ORCA 调度器将所有这些因素都考虑在内:它根据到达时间最多选择 “最大批量大小 ”的请求,同时为首次调度的请求预留足够的空间来存储密钥和值。我们在算法 1 中描述了调度过程。该算法从请求池中选择一批请求(第 4 行)并调度这批请求(第 5 行)。选择函数(第 17 行)根据请求的到达时间从请求池中选择最多 max_bs 个请求(第 20-22 行)。算法 1 并没有描述请求到达和返回的过程;我们可以将其理解为存在并发线程,将新到达的请求插入请求池,并将已完成的请求从请求池中移除。
当调度器认为一个请求处于启动阶段(即该请求尚未被调度)时,调度器会使用请求的 max_tokens属性提前预留 GPU 内存中用于存储密钥和值的 max_tokens 插槽(第 23-26 行)。调度程序会根据 n_rsrv(当前预留插槽的数量)确定是否可以预留插槽(第 25 行),其中插槽的定义是存储单个令牌的 Attention 密钥和值所需的内存量。在这里,n_slots 是由 ORCA 系统操作员调整的参数,表示分配给注意力 K/V 管理器的内存区域大小(以插槽为单位)。由于一个请求中的令牌数量不能超过 max_tokens,如果可以预留,则保证管理器可以为新生成的密钥和值分配缓冲区,直到请求结束。

与需要量化延迟和吞吐量之间权衡的 max_bs 的调整不同,ORCA 系统操作员无需任何实验即可轻松配置 n_slots。给定模型规格(如隐藏大小、层数等)以及层内和层间并行程度,ORCA 的 GPU 内存使用量主要取决于 n_slots。也就是说,操作员只需在内存限制下使用尽可能大的 n_slots。
流水线并行
ORCA 的调度器使引擎中的worker在多个批次中流水线执行。调度程序不会等待已调度批次的返回,直到 n_scheduled(当前已调度批次的数量)达到 n_workers(算法 1 第 9-10 行)。这样,调度程序就能将引擎中并发运行的批次数保持在 n_workers,这意味着引擎中的每个 Worker 都在处理其中一个批次,而不会闲置。
图 8a 描述了 3 个 ORCA 工作者的执行流水线,最大批次大小为 2。 我们假设请求 A 在 B 之前到达,B 在 C 之前到达,以此类推。首先,调度器根据到达时间选择请求 A 和 B,并调度引擎处理一批请求 A 和 B(我们称之为 AB 批),由 Worker1、Worker2 和 Worker3 依次处理这批请求。只有在调度器注入另外两个批次后,调度器才会等待 AB 批次的返回: CD 和 EF。一旦批次 AB 返回,请求 A 和请求 B 就会再次被选中并调度,因为它们是请求池中最早到达的请求。

相比之下,当前服务系统和执行引擎(例如 Triton [7] 和 FasterTransformer [4] 的组合)之间的接口由于请求级调度的原因,不允许在当前运行的批次结束之前注入另一个批次。也就是说,在当前批次 AB 完成之前,Triton 无法向 FasterTransformer 注入下一个请求 C。为了在这种限制条件下流水线式执行多个层间分区,FasterTransformer 将一批请求分成多个微批次[28],并在微批次间流水线式执行分区。在图 8b 中,FasterTransformer 将批次 AB 拆分为两个微批次 A 和 B。由于每个分区都以批次方式处理一个微批次(比原始批次小),因此批次带来的性能增益可能会变小。此外,当微批尺寸过大时,这种方法可能会在管道中插入气泡,使微批数量少于分区数量。FasterTransformer 需要用批处理效率(更大的微批大小)来换取流水线效率(更少的流水线气泡),而 ORCA 由于采用了迭代级调度,因此不需要这样的权衡,可以轻松地对请求进行流水线处理,而无需将批处理分成微批。
Implementation
处理通信:
用了gRPC [2] 进行通信cpu和gpu,用cpu线程发布gpu内核的控制信息
用NCCL实现层间和层内通信。
我们基于 CUDA 生态系统,用 13K 行 C++ 实现了 ORCA。我们在 ORCA 引擎的控制平面使用 gRPC [2] 进行通信,而在数据平面使用 NCCL [5],用于层间和层内通信。由于我们设计 ORCA 的重点是基于transformer的生成模型,因此 ORCA 提供了流行的transformer层作为模型的构建模块,包括原始的encoder-decoder 架构的transformer[60]、GPT[47]以及 Raffel 等人[49]中讨论的其他变体。
三种融合:
为 LayerNorm、Attention 和 GeLU 算子实现了融合内核
融合注意力算子(计算注意力查询和关键字之间的点积、点积上的 Softmax 以及注意力值的加权平均值的过程被融合)
融合不同请求的内核的所有线程块中拆分的注意力算子,虽然CUDA 编程实践通常不鼓励这种做法,因为它们处理的张量形状各不相同,但我们发现这种融合有益于提高 GPU 利用率和减少内核启动开销
我们还为 LayerNorm、Attention 和 GeLU 算子实现了融合内核,就像其他用于训练或推理transformer模型的系统一样[1, 4, 58]。例如,计算注意力查询和关键字之间的点积、点积上的 Softmax 以及注意力值的加权平均值的过程被融合到注意力算子的单个 CUDA 内核中。此外,我们还更进一步,通过简单地连接不同请求的内核的所有线程块,来融合拆分的注意力算子的内核。虽然这种融合会使内核中的线程块具有不同的特性和生命周期(CUDA 编程实践通常不鼓励这种做法),因为它们处理的张量形状各不相同,但我们发现这种融合有益于提高 GPU 利用率和减少内核启动开销 [34,39]。
Evaluation
在这个部分中,我们将介绍评估结果,以显示 ORCA 的效率。
环境
我们在 Azure ND96asr A100 v4 虚拟机上进行评估,每个虚拟机配备 8 个通过 NVLink 连接的英伟达™(NVIDIA®)40-GB A100 GPU。根据测试机型的大小,我们最多使用四个虚拟机。每个虚拟机有 8 个 Mellanox 200Gbps HDR Infiniband 适配器,可在虚拟机之间提供 1.6Tb/s 的互连带宽。
模型
在整个实验中,我们使用 GPT [12] 作为基于变换器的生成模型的代表。我们使用了不同配置的 GPT 模型,如表 1 所示。13B 和 175B 模型的配置来自 GPT-3 论文[12]。在这两个模型的基础上,我们改变了层数和隐藏大小,形成了 101B 和 341B 模型的配置。所有模型的最大序列长度都是 2048,这与原始文献[12]的设置一致。我们在实验中使用了 fp16 格式的模型参数和中间激活。我们还采用了第 4.1 节中描述的层间和层内并行策略,只有 13B 模型适合 GPU。例如,175B 模型通过使用 2 个层间分区细分为 8 个层内分区,总共在 16 个 GPU 上进行分区,其中同一虚拟机中的 8 个 GPU 属于同一层间分区。

基准系统
我们与 FasterTransformer [4]进行了比较,后者是一个通过分布式执行支持大规模 Transformer 模型的推理引擎。虽然还有其他支持分布式执行的系统,如 Megatron-LM [3] 和 DeepSpeed [1],但这些系统主要是针对训练工作负载设计和优化的,因此与推理优化的系统相比,性能相对较低。
情景
第一个场景和FasterTransformer 对比处理批处理(而非单个请求)所需的时间
我们使用两种不同的场景来进行评估。首先,我们设计了一个微基准,在不受迭代级调度影响的情况下,只评估 ORCA 引擎的性能。特别是,在这种情况下,我们不运行 ORCA 调度器。相反,在给定一批请求的情况下,测试脚本会重复向 ORCA 引擎注入相同批次的请求,直到批次中的所有请求都处理完毕,模仿典型请求级调度的行为。我们还假设批次中的所有请求具有相同数量的输入标记,并生成相同数量的输出标记。我们报告了处理批处理(而非单个请求)所需的时间,并将结果与 FasterTransformer [4] 进行了比较。
泊松过程意味着在模拟或分析中使用了一种统计模型来捕捉随机事件的到达模式
请求的到达虽然是在请求完成后到达,但是可以被认为是随机的,尤其是在许多实际应用场景中,如网络服务器接收请求、电话交换机接收呼叫等。这里所说的“随机”并不意味着请求完全不可预测或没有规律,而是指请求的到达时间间隔遵循某种统计分布,而泊松过程就是描述这种随机到达现象的一种常用模型。
第二个场景通过模拟工作负载来测试 ORCA 的端到端性能。由于生成式语言模型没有公开的请求跟踪,因此我们合成了客户端请求跟踪。合成跟踪中的每个请求都是通过采样输入标记数和 max_gen_tokens 属性随机生成的,其中输入标记数加上 max_gen_tokens 等于第 4.2 节中描述的 max_tokens 属性。我们假设所有请求都会继续生成,直到生成的标记数达到 max_gen_tokens。换句话说,我们让模型永远不发出“”标记。这是因为我们既没有实际的模型检查点,也没有实际的输入文本,所以我们没有任何信息来猜测生成“”标记的正确时间。请求生成后,我们根据泊松过程设置请求到达时间,从而合成跟踪。 为了评估 ORCA 在不同负载下的行为,我们改变泊松参数(即到达率)并相应调整请求到达时间。我们使用从不同分布生成的多个跟踪报告延迟和吞吐量,以便更好地比较和理解 ORCA 和 FasterTransformer 的行为。
对于第一个场景进行对比:Engine Microbenchmark
我们首先使用第一种情况比较 FasterTransformer 和 ORCA 引擎的性能。我们将批次中的所有请求设置为具有相同数量的输入标记(32 或 128),并生成 32 个标记。也就是说,在这组实验中,批次中的所有请求都是同时开始和完成处理的。我们使用三种不同的模型进行实验: 13B、101B 和 175B。对于每个模型,我们都使用了表 1 所示的相应并行化策略。
图 9 显示了 FasterTransformer 和 ORCA 引擎处理由相同请求组成的批处理的性能。在图 9a 中,ORCA 引擎在所有配置下的性能都与 FasterTransformer 相近(或略差)。这是因为 ORCA 不对 Attention 操作应用批处理,而 FasterTransformer 则对所有操作应用批处理。不过,性能差异相对较小。尽管没有对注意力操作进行批处理,但由于注意力中没有模型参数,因此这一决定对效率影响不大,因为在多个请求中重复使用模型参数没有任何好处。

图 9b 显示了在单个虚拟机中使用全部 8 个 GPU 的 101B 模型的类似结果。从这些结果可以看出,ORCA 引擎和 FasterTransformer 在实现 CUDA 内核和层内分区通信方面的效率相当。需要注意的是,FasterTransformer 在 13B 模型中不能使用 8 或更大的批量大小(在 101B 模型中不能使用 16 或更大的批量大小),因为每个请求的 Attention 键和值都有固定的内存预分配量,其增长与模型的最大序列长度(本例中为 2048)成正比。相比之下,ORCA 根据 max_tokens 属性为每个请求分别设置键和值的缓冲区大小,从而避免了多余的内存分配。
接下来,我们进一步尝试 175B 模型,该模型将各层分成两个层间分区。在这种情况下,为了更好地进行比较,我们禁用了两个系统的层间分区流水线执行。对于 FasterTransformer,我们将微批次的大小设置为等于批次大小,以禁用流水线执行。如图 9c 所示,ORCA 引擎的性能比 FasterTransformer 高出 47%。我们将这一性能提升归功于第 4.1 节所述的控制数据平面分离。我们省略了 341B 模型,因为它与 175B 模型的结果类似。
总结一下这个实验想表达如图:
对于第二个场景进行对比:端到端的性能
现在,我们通过测量不同负载下合成请求跟踪的延迟和吞吐量来评估 ORCA 的端到端性能。在合成跟踪时,我们从 U(32;512)(范围从 32 到 512(含)的均匀分布)中对每个请求的输入令牌数进行采样。max_gen_tokens 属性从 U(1;128) 中采样,这意味着耗时最少的请求和耗时最多的请求分别需要模型迭代 1 次和 128 次来处理。
在FasterTransformer中是没有调度器的,为了对比创建了一个自定义调度程序,采用的是最常用的调度算法。
而ORCA自己包含了调度器。
与微基准测试不同,为了测量端到端的性能,我们测试了包括 ORCA 调度器在内的整个 ORCA 软件栈。客户端请求按照上述合成轨迹到达 ORCA 调度器。我们报告了各种最大批量配置的结果。对于没有自己调度程序的 FasterTransformer,我们实施了一个自定义调度程序,该程序接收客户端请求、创建批次并将批次注入 FasterTransformer 实例。我们让自定义调度器从请求队列中提取最大批次大小的请求,从而动态创建批次,这是现有服务系统(如 Triton [7] 和 TensorFlow Serving [42])最常用的调度算法。我们再次报告了各种最大批量大小配置以及不同微批量大小的结果,微批量大小是 FasterTransformer 中用于控制流水线行为的附加旋钮(为了让批次处理效果更好)。
图10显示了端到端延迟和吞吐量的中位数。由于跟踪中的每个请求需要不同的处理时间,这(大致)与生成令牌的数量成比例,因此我们根据每个请求生成令牌的数量规范化报告延迟中值。从图中,我们可以看到ORCA提供了比FasterTransformer更高的吞吐量和更低的延迟。唯一的例外是低负载下的101B模型(图10a)。在这种情况下,ORCA和FasterTransformer都没有足够数量的请求来批处理。也就是说,延迟将主要取决于引擎的性能,如图9b所示。当负载变得更重时,ORCA提供更高的吞吐量,而延迟的增加相对较小,因为ORCA调度器会让延迟到达的请求搭上当前正在进行的批处理的顺风车。相比之下,FasterTransformer无法有效地处理(1)在不同时间到达的多个请求;(2)需要不同的迭代次数来完成;或者(3)从不同数量的输入令牌开始,导致峰值吞吐量为0.49 req/s和更高的延迟。如果我们使用使用多个层间分区的175B或341B模型(图10b和图10c), ORCA在每个级别的负载下在延迟和吞吐量方面都优于fasttransformer,当我们比较类似延迟级别的结果时,吞吐量会提高一个数量级。例如,为了匹配175B模型的中位数规范化延迟190ms,这是图9c中“orca(128)”规范化执行时间(按生成令牌的数量计算)的两倍,FasterTransformer提供了0.185 req/s的吞吐量,而orca提供了6.81 req/s的吞吐量,这是36.9倍的加速。
得出结论, ORCA在每个级别的负载下在延迟和吞吐量方面都优于fasttransformer
不同的批量大小配置
图 10 显示,增加 ORCA 的最大批量大小可提高吞吐量,但不会影响延迟。这是因为 ORCA 的迭代级调度解决了提前完成和延迟加入请求的问题。不过,对于任意硬件设置、模型和工作负载,并不能保证增加批量大小不会对延迟产生负面影响。如调度算法部分所述,必须同时考虑所需的延迟和吞吐量要求,谨慎设置最大批量大小。
有趣的是,扩大 FasterTransformer 的最大批量并不一定有助于提高吞吐量。通过测试不同负载下所有模型的最大批量大小(max_bs)和微批量大小(mbs)的所有可能组合,我们发现 (max_bs, mbs) = (1, 1) 或 (8, 8) 是最佳选择。根据我们在分布式架构中的讨论,FasterTransformer 基于微批处理的流水线效率可能较低,因为引擎最多只能以批处理方式处理 mbs 数量的请求,这就解释了为什么具有最大可能 mbs(与 max_bs 相同)的配置比其他配置具有更好的性能。此外,虽然增加 max_bs 可以提高批处理量,从而提高性能,但同时也会增加批处理输入令牌数量或生成令牌数量差异较大的请求的可能性。在这种情况下,FasterTransformer 无法高效地处理批处理,因为:(1) 在批处理的第一次迭代中,FasterTransformer 会把所有请求都当作与最短请求具有相同输入长度的请求来处理;(2) 提前完成的请求无法立即返回客户端。
同构请求的跟踪
在请求大小相同的情况下,FasterTransformer的性能会随着max_bs的增加而变好,orca也会,但是使用最大批处理大小为1的情况时候ORCA退化为不执行批处理的worker,而性能没那么好,但也比FasterTransformer (max_bs=8)好
我们在使用同构请求跟踪时测试ORCA和fasttransformer的行为,即跟踪中的所有请求具有相同数量的输入令牌和相同的max_gen_tokens属性。由于所有请求都需要相同数量的迭代来完成处理,因此在此跟踪中不会出现提前离开请求的问题。结果,现在max_bs的增加对FasterTransformer的性能产生了显著的积极影响,如图11所示。尽管如此,除了使用最大批处理大小为1的情况外,ORCA仍然优于FasterTransformer (max_bs=8),在这种情况下,ORCA退化为不执行批处理的ORCA工人的简单管道。

Related Work and Discussion
递归模型的细粒度批处理
我们想重点介绍 BatchMaker [23],它是之前最相关的工作之一。BatchMaker 是一个用于 RNN 的服务系统,可在 RNN 单元的粒度上执行调度和批处理,其灵感来自 RNN 重复相同计算的独特特性。请求到达后,BatchMaker 会将用于处理请求的数据流图分解成 RNN 单元,以单元(而不是整个图)为粒度进行调度,并对相同单元(如有)的执行进行批处理。由于每个 RNN 单元总是执行完全相同的计算,因此无论单元的位置(即标记索引)如何,BatchMaker 都能批量执行多个 RNN 单元。通过这种方式,BatchMaker 允许新到达的 RNN 请求加入(或完成的请求离开)当前正在执行的批处理,而无需等待批处理完全结束。
但是,BatchMaker 无法为Transformer模型制作成批的单元,因为图中有太多不同的单元(封装了处理标记的计算的子图;图 1c)。不同标记索引 t 的每个单元必须使用不同的注意键/值集。由于每个 t 的单元都不同,因此图中包含 L 个不同的单元(L 表示输入和生成令牌的数量),从而大大降低了同一计算单元在给定时刻出现的可能性(例如,在图 10 中,L 的范围从 33 = 32+1 到 640 = 512+128)。因此,单元的执行大部分将被序列化,从而使 BatchMaker 回归到非批处理执行。BatchMaker 也不支持需要模型和流水线并行的大型模型。
BatchMaker 的目标是检测和排列可批量处理的 RNN 单元,而我们设计 ORCA 的关键原则是在每一轮模型参数读取中执行尽可能多的计算。 这是因为我们了解到,对于大规模模型而言,从 GPU 全局存储器读取参数是端到端执行时间的主要瓶颈。根据这一原则,我们采用迭代级调度和选择性批处理,在一轮参数读取中处理所有 “准备就绪 ”的令牌,而不管令牌的处理是否可以批处理(非注意力操作)(注意力操作)。
Transformer模型的专用执行引擎
推理系统也是推理系统背后的后端执行引擎:FasterTransformer 、LightSeq 、TurboTransformers和 EET
现在的服务系统是处理客户端请求和调度底层执行引擎执行的抽象系统,如 :Triton Inference Server 和 TensorFlow Serving 和 Clipper
基于Transformer的模型的出色性能促使人们为它们开发专门的推理系统。FasterTransformer [4]、LightSeq [61]、TurboTransformers [22] 和 EET [36] 就是这样的例子。这些系统都是现有服务系统(如 Triton Inference Server [7] 和 TensorFlow Serving [42])的后端执行引擎。也就是说,这些系统将调度角色委托给服务系统层,遵循典型的请求级调度。相反,ORCA 建议以更细的粒度调度执行,这在当前的系统中是不可能实现的,除非改变调度器与执行引擎之间的协调机制。请注意,在这些系统中,FasterTransformer 是唯一支持分布式执行的系统。虽然 Megatron-LM [3] 和 DeepSpeed [1] 等系统也可用于分布式执行,但这些系统主要是为大规模训练而非推理服务而优化的。
选FasterTransformer作为对比的原因:因为FasterTransformer 是唯一支持分布式执行的且在推理时实现优化的系统。虽然 Megatron-LM、 DeepSpeed等系统也可用于分布式执行,但这些系统主要是为大规模训练而非推理服务而优化的。
服务系统与执行引擎之间的接口
当前的通用服务系统,如 Triton Inference Server [7] 和 Clipper [16],是处理客户端请求和调度底层执行引擎执行的抽象系统。通过分离服务层和执行层的设计与实现,这种方法被认为是有益的。然而,我们发现,这两层之间的普遍接口对于处理像 GPT [12]这样具有多迭代特性的模型来说限制太多。相反,我们设计的 ORCA 将调度器和引擎紧密结合在一起,简化了两种建议技术的应用:迭代级调度和选择性批处理。在本文中,我们没有研究在不丢失抽象分离的情况下支持这两种技术的通用接口设计,但探索这种可能性可能是一个有趣的课题;我们将此问题留待未来工作中讨论。
它想表达虽然设计的 ORCA 将调度器和引擎紧密结合在一起,简化了两种建议技术的应用:迭代级调度和选择性批处理。
未来可以做的方向:研究在不丢失抽象分离的情况下支持这两种技术的通用接口设计。
在软件开发和设计的语境下,指的是一个设计良好的接口,它能够在支持特定技术或功能的同时,保持系统组件之间的抽象层次和分离性。
总结
本文提出了具有选择性批处理功能的迭代级调度,这是一种新颖的方法,可为基于 Transformer 的生成模型提供低延迟和高吞吐量。迭代级调度使调度程序在迭代而非请求的粒度上与执行引擎交互,而选择性批处理则能批处理在不同位置处理标记的任意请求,这对于在迭代级调度中应用批处理至关重要。基于这些技术,设计并实现了一个名为 ORCA 的分布式服务系统。实验证明了我们方法的有效性: 在延迟相同的情况下,ORCA 的吞吐量比目前最先进的系统高出一个数量级。

