论文Ray一个分布式AI应用程序框架
Length: • 10 mins
Annotated by niuzhidao
Ray:新兴人工智能应用的分布式框架
Philipp Moritz∗、Robert Nishihara∗、Stephanie Wang、Alexey Tumanov、 Richard Liaw、Eric Liang、Melih Elibol、杨宗衡、William Paul、 Michael I. Jordan、Ion Stoica
arXiv:1712.05889v2 [cs.DC] 2018 年
加州大学伯克利分校
摘要
下一代人工智能应用程序将不断与环境交互,并从这些交互中学习。这些应用在性能和灵活性方面提出了新的、苛刻的系统要求。在本文中,我们考虑了这些需求并提出了 Ray——一个分布式系统来解决这些需求。 Ray 实现了一个统一的接口,可以表达任务并行和基于参与者的计算,并由单个动态执行引擎支持。为了满足性能要求,Ray采用分布式调度器和分布式容错存储来管理系统的控制状态。在我们的实验中,我们展示了每秒处理超过 180 万个任务的能力,并且在几个具有挑战性的强化学习应用程序中比现有的专用系统具有更好的性能。
介绍
在过去的二十年中,许多组织一 直在收集并旨在利用不断增长的数据量。这导致了大量分布式数据分析框架的开发,包括批处理[20,64,28]、流式处理 [15,39,31]和图[34,35,24]处理系统。这些框架的成功使组织能够将分析大数据集作为其业务或科学战略的核心部分,并迎来了“大数据”时代。 最近,以数据为中心的应用程序的范围已经扩展到涵盖更复杂的人工智能(AI)或机器学习(ML)技术[30]。典型案例是监督学习,其中数据点附有标签,并且将数据点映射到标签的主力技术由深度神经网络提供。这些深度网络的复杂性导致了另一场专注于深度神经网络训练的框架的出现 ∗同等贡献 以及它们在预测中的应用。这些 框架通常利用专用硬件(例如 GPU 和 TPU),目的是减少批量设置中的训练时间。示例包括 TensorFlow [7] 、MXNet [18] 和 PyTorch [46]。然而,人工智能的前景比传统的监督学习要广泛得多。新兴人工智能应用程序必须越来越多地在动态环境中运行,对环境变化做出反应,并采取一系列行动来实现长期目标 [8, 43]。他们不仅要致力于利用收集到的数据,还要探索可能采取的行动的空间。这些更广泛的要求自然地被纳入强化学习 (RL) 的范式中。强化学习涉及基于延迟和有限的反馈来学习在不确定的环境中连续运行[56]。基于强化学习的系统已经取得了显着的成果,例如谷歌的 AlphaGo 击败了人类世界冠军 [54], 并开始进入对话系统、无人机 [42] 和机器人操纵 [25, 60]。 强化学习应用的核心目标是学习一种策略——从环境状态到行动选择的映射——随着时间的推移产生有效的性能,例如赢得比赛或驾驶无人机。在大规模应用中找到有效的策略需要三个主要能力。首先,强化学习方法通常依赖模拟来评估政策。模拟使得探索动作序列的许多不同选择并了解这些选择的长期后果成为可能。其次,与监督学习算法一样,强化学习算法需要执行分布式训练,以基于通过模拟或与物理环境交互生成的数据来改进策略。👉三,政策的目的是为控制问题提供解决方案,因此需要将政策服务于交互式的闭环和开环控制场景。 这些特性推动了新的系统要求:强化学习系统必须支持细粒度计算(例如,在与现实世界交互时以毫秒为单位渲染动作,并执行大量模拟) 计算),必须支持时间(例如, 模拟可能需要几毫秒或几小时)和资源使用(例如,用于训练的 GPU和用于模拟的 CPU)方面的异构性,并且必须支持动态执行,作为模拟或与模型交互的结果环境可以改变未来的计算。因此,我们需要一个动态计算框架,能够以毫秒级延迟每秒处理数百万个异构任务。 为大数据工作负载或监督学习工作负载开发的现有框架无法满足强化学习的这些新要求。 MapReduce [20] 、 Apache Spark [64] 和 Dryad [28] 等批量同步并行系统不支持细粒度模拟或策略服务。 CIEL [40] 和 Dask [48] 等任务并行系统对分布式训练和服务提供的支持很少。对于 Naiad [39] 和 Storm [31] 等流媒体系统也是如此。 TensorFlow [7] 和 MXNet [18] 等分布式深度学习框架天然不支持模拟和服务。最后,TensorFlow Serving [6] 和 Clipper [19] 等模型服务系统既不支持训练也不支持模拟。 虽然原则上可以通过将多个现有系统拼接在一起来开发端到端解决方案(例如,用于分布式训练的 Horovod [53]、用于服务的 Clipper [19] 以及用于模拟的 CIEL [40]),但实际上这种方法由于应用程序中这些组件的紧密耦合,这是站不住脚的。因此,今天的研究人员和从业者为专门的 RL 应用构建了一次性系统 [58,41,54,44,49,5]。这种方法本质上将调度、容错和数据移动等标准系统挑战推到每个应用程序上,从而给分布式应用程序的开发带来了巨大的系统工程负担。 在本文中,我们提出了 Ray,这是一种通用集群计算框架,可以为 RL 应用程序提供模拟、训练和服务。这些工作负载的要求范围从轻量级和无状态计算(例如模拟)到长时间运行和有状态计算(例如训练)。为了满足这些要求,Ray 实现了一个统一的接口,可以表达任务并行和基于参与者的计算。任务使 Ray 能够高效、动态地负载平衡模拟、处理大量输入和状态空间(例如图像、视频)并从故障中恢复。相比之下,参与者使 Ray 能够有效地支持有状态计算,例如模型训练,并向客户端公开共享的可变状态(例如参数服务器)。 Ray 在高度可扩展和容错的单个动态执行引擎之上实现了参与者和任务抽象。 为了满足性能要求,Ray 分布了两个通常集中在现有框架中的组件 [64,28,40]:(1) 任务调度程序和 (2) i+1 (观察 奖励 (ri+1) 轨迹: s0, (s1, r1), ..., (sn, rn) 服务 政策评估 训练 政策改进 (例如 新元)模环拟境
)
状态(s )
动作(ai)
代理
人政 策
图 1:强化学习系统示例。
元数据存储维护计算沿袭和数据 对象的目录。这使得 Ray 能够以毫秒级的延迟每秒调度数百万个任务。此外,Ray 为任务和参与者提供基于沿袭的容错,为元数据存储提供基于复制的容错。
虽然 Ray 支持 RL 应用程序环境中的服务、训练和模拟,但这并不意味着它应该被视为在其他环境中为这些工作负载提供解决方案的系统的替代品。特别是, Ray 并不打算取代 Clipper [19] 和 TensorFlow Serving [6] 等服务系统,因为这些系统解决了部署模型时更广泛的挑战,包括模型管理、测试和模型组合。同样,尽管 Ray 具有灵活性,但它并不能替代通用数据并行框架,例如 Spark [64],因为它目前缺乏这些框架提供的丰富功能和 API
(例如,散乱缓解、查询优化)。我们做出以下贡献:
我••们设计并构建了👉一个分布式 框架,它统一了训练、模拟和服务——新兴强化学习应用程序的必要组件。为了支持这些工作负载,我们在动态任务执行引擎之上统一了参与者和任务并行抽象。
- 为了实现可扩展性和容错性,我们提出了一种系统设计原则,其中控制状态存储在分片元数据存储中,所有其他系统组件都是无状态的。
- 为了实现可扩展性,我们提出了一种自下而上的分布式调度策略。
动机和要求
我们首先考虑 RL 系统的基本组件,并充实 Ray 的关键要求。如图 1 所示,在 RL 设置中,代理与环境重复交互。代理的目标是学习最大化奖励的策略。一个政策是 // 通过与 env 交互来评估策略。 (例 如模拟器) 推出(政策、 环境):轨迹=[] 状态 = 环境.initial_state()while
(notenvironment.has_termerated()): action = policy.compute(state) //
服务 state,reward =environment.step(action) // 模拟 轨迹.append(状态,奖励) 返 轨 迹// 迭代改进策略 直到收敛 train_policy(环境): 政策=初始政策() while(策略尚未收 敛): 轨迹=[] 对于从 1 到 k 的 i: // 通过生成 k 个 rollout 来评估策略 trajectories.append(rollout(策略、环境)) // 改进策略 policy =policy.update(trajectories) // 训练 返 策 略
图 2:用于学习策略的典型 RL 伪代 码。 从环境状态到行动选择的映射。环境、代理、状态、动作和奖励的精确定义是特定于应用程序的。 为了学习策略,代理通常采用两步过程:(1)策略评估和(2)策略改进。为了评估策略,代理与环境交互(例如,通过环境模拟)来生成轨迹,其中轨迹由当前策略生成的一系列(状态、奖励)元组组成。然后,代理使用这些轨迹来改进策略;即,在最大化奖励的梯度方向上更新策略。图 2 显示了代理用于学习策略的伪代码示例。该伪代码通过调用 rollout(environment,policy) 来生成轨迹来评估策略。 trainpolicy() 然后使用这些轨迹通过 policy.update(trajectories) 改进当前策略。重复此过程直到策略收敛。 因此,强化学习应用的框架必 须为训练、服务和模拟提供有效的支持(图 1)。接下来,我们简要描述这些工作负载。 训练通常涉及运行随机梯度下降 (SGD)(通常在分布式环境中)来更新策略。分布式 SGD 通常依赖于 allreduce 聚合步骤或参数服务器 [32]。 服务使用经过训练的策略来根 据环境的当前状态呈现操作。服务系统的目标是最小化延迟并最大化每秒的决策数量。为了扩展,通常在服务该策略的多个节点之间平衡负载。 最后,大多数现有的 RL 应用 程序使用模拟来评估策略——当前的 RL 算法并不适用。 样本效率足以完全依赖于与物理世界交互获得的数据。这些模拟的复杂性差异很大。它们可能需要几毫秒(例如,模拟国际象棋游戏中的一步)到几分钟(例如,模拟自动驾驶汽车的真实环境)。 与监督学习不同,在监督学习中,训练和服务可以由不同的系统单独处理,而在强化学习中,所有这三个工作负载都紧密耦合在单个应用程序中,并且它们之间具有严格的延迟要求。目前,没有框架支持这种工作负载耦合。理论上,可以将多个专用框架拼接在一起以提供整体功能,但实际上,在强化学习的背景下,系统之间的数据移动和延迟是令人望而却步的。因此,研究人员和从业者一直在构建自己的一次性系统。 这种情况需要开发新的强化学习分布式框架,以有效支持训练、服务和模拟。特别是,这样的框架应满足以下要求: 细粒度、异构计算。计算的持续时间可以从几毫秒(例如,采取行动)到几小时(例如,训练复杂的策略)。此外,训练通常需要异构硬件(例如 CPU、GPU 或 TPU)。 灵活的计算模型。强化学习应用程序需要无状态和有状态计算。无状态计算可以在系统中的任何节点上执行,这使得在需要时可以轻松实现负载平衡和计算到数据的移动。因此,无状态计算非常适合细粒度模拟和数据处理,例如从图像或视频中提取特征。相比之下,状态计算非常适合实现参数服务器、对 GPU 支持的数据执行重复计算或运行不公开其状态的👉三方模拟器。 动态执行。强化学习应用程序的多个组件需要动态执行,因为计算完成的顺序并不总是事先知道 (例如,模拟完成的顺序),并 且计算的结果可以确定未来的计算(例如,模拟将决定我们是否需要执行更多模拟)。 我们最后提出两点意见。首先,为了在大型集群中实现高利用率,这样的框架必须每秒处理数百万个任务。∗ 其次,这样的框架并不是为了从头开始实现深度神经网络或复杂的模拟器。相反,它应该能够与现有模拟器 [13,11,59] 和深度学习框架 [7,18,46,29] 无缝集成。 ∗假设5ms单核任务和200个32核节点的集群。该集群可以运行 (1s/5ms)×32×200 = 1.28M 任务/秒。
表 1:Ray API姓名
描述
期货 = f.remote(args)
远程执行函数f。 f.remote() 可以将对象或 future 作 为输入
并返
一个或多个期货。这是非阻塞的。
对象 = ray.get(futures)
返
与一个或多个 future 关联的值。这是阻塞。
准备好的 futures = ray.wait(futures, k, timeout)
返
相应任务已完成的 future
k 已完成或超时到期。
演员 = Class.remote(args)
期货 = actor.method.remote(args)
将类 Class 实例化为远程参与者,并返
它的 句柄。调用一个方法在远程参与者上并返
一个或多个 future。两者都是非阻塞的。
编程和计算模型
Ray 实现了动态任务计算模型,即将应用程序建模为在执行过程中演变的依赖任务
。在此模型之上,Ray 提供了参与者和任务并行编程抽象。这种统一将 Ray 与 CIEL 等相关系统(仅提供任务并行抽象)以及 Orleans [14] 或 Akka [1](主要提供参与者抽象)区分开来。
编程模型
任务。任务代表在无状态工作线程上执行远程函数。当调用远程函数时,会立即返表示任务结果的 future。可以使用 ray.get() 检索 Future,并将其作为参数传递给其他远程函数,而无需等待其结果。这允许用户在捕获数据依赖性的同时表达并行性。表 1 显示了 Ray 的 API。 远程函数对不可变对象进行操作,并且期望是无状态且无副作用的:它们的输出仅由它们的输入决定。这意味着幂等性,它通过在失败时重新执行函数来简化容错能力。
演员。一个参与者代表一个有状 态的计算。每个参与者公开可以远程调用并串行执行的方法。方法执行类似于任务,因为它远程执行并返 future,但不同之处在于它在有状态工作线程上执行。 Actor的句柄可以传递给其他 Actor 或任务,使它们可以调用该 Actor 上的方法。
表 2 总结了任务和参与者的属性。任务通过利用任务粒度的负载感知调度、输入数据局部性(因为每个任务都可以在存储其输入的节点上进行调度)以及低恢复开销(因为不需要检查点和恢复中间状态)来实现细粒度负载平衡。相比之下,参与者提供了更高效的细粒度更新,因为这些更新是在内部状态而不是外部状态上执行的,而外部状态通常需要序列化和反序列化。例如,actor 可用于实现参数服务器 [32] 和基于 GPU 的迭代计算(例如训练)。此外,参与者还可用于包装👉三方模拟器和其他难以序列化的不透明句柄。 为了满足异构性和灵活性的要求(👉 2 节),我们通过三种方式增强 API。首先,为了处理具有异构持续时间的并发任务,我们引入了 ray.wait(),它等待前 k 个可用结果,而不是像 ray.get() 那样等待所有结果。其次,为了处理资源异构任务,我们使开发人员能够指定资源需求,以便Ray调度器能够有效地管理资源。👉三,为了提高灵活性,我们启用嵌套远程函数,这意味着远程函数可以调用其他远程函数。这对于实现高可扩展性(👉 4 节)也至关重要,因为它允许多个进程以分布式方式调用远程函数。任务(无状态)
演员(有状态)
细粒度负载均衡
粗粒度负载均衡
支持对象局部性
地方支持薄弱
小更新的开销很高
小更新开销低
高效的故障处理
检查点的开销
计算模型
Ray 采用动态任务
计算模型 [21],其中远程函数和参与者方法的执行在输入可用时由系统自动触发。在本节中,我们将描述如何从用户程序(
3)构建计算
(
4)。该程序使用表 1 中的 API 来实现 2 中的伪代码。 首先忽略参与者,计算
中有两种类型的节点:数据对象和远程函数调用或任务。边也有两种类型:数据边和控制边。数据边缘捕获de- 表 2:任务与参与者的权衡。 政策2 A20 模拟 器 A22 推出 ... T3 更新策 略 ... ...
推出22
推出12
推出11
政策1
A12
推出
T2
更新策 略
推出21
A21
推出
A11
推出
任务/方法
状态边 缘
T1
创建策 略
A10
模拟 器
T0
火车政 策
目的
数据边缘 控 制边
@ray.remote
def create_policy():
# 随机初始化策略。
退货政策
@ray.remote(num_gpus=1) 类模拟器(对象):
定义初始化(自身):
# 初始化环境。
self.env = 环境()
def rollout(self, 策略, num_steps): 观察结果
= []
观察 = self.env.current_state() for _ in range(num_steps):
行动 = 政策(观察) 观察 =
self.env.step(行动) 观察.追加( 观察)返观察结果
@ray.远程(num_gpus=2)
def update_policy(策略, *rollouts):
# 更新策略。
退货政策
@ray.remote
定义 train_policy():
# 创建策略。
policy_id = create_policy.remote()
# 创建 10 个演员。
模拟器 = [Simulator.remote() for _ in range(10)]
# 进行 100 步训练。
对于 _ 在范围(100)内:
# 对每个 actor 执行一次 rollout。
rollout_ids = [s.rollout.remote(policy_id)
对于模拟器中的 s]
# 通过推出更新策略。
政策 ID =
update_policy.remote(policy_id, *rollout_ids)返 ray.get(policy_id)
3:在 Ray 中实现 2 中示例的 Python 代码。请注意,@ray.remote 表示远程函数和参与者。远程函数和参与者方法的调用返 futures,它可以传递给后续的远程函数或参与者方法来编码任务依赖关系。每个参与者都有一个在其所有方法之间共享的环境对象 self.env 。
数据对象和任务之间的依赖性。 更准确地说,如果数据对象 D 是任务 T 的输出,我们添加从 T 到 D的数据边。类似地,如果 D 是 T 的输入,我们添加从 D 到 T 的数据边。控制边捕获由嵌套远程函数产生的计算依赖性(👉 3.1 节):如果任务 T1调用任务1,则我们添加从 T1到1的控制边。
Actor 方法调用也表示为计算中的节点。它们与任务相同,但有一个关键区别。为了捕获同一参与者上后续方法调用的状态依赖关系,我们添加了
i
j
👉三种类型的边:有状态边。如果在同一个 actor上的方法 Mi之后立即调用方法 Mj,那么我们将添加一条从 M 到 M 的有状态边。因此,所有
4:与 3 中的 train policy.remote() 调 用相对应的任务
。远程函数调用和参与者方法调用对应于任务
中的任务。该
显示了两名演员。每个参与者的方法调用(标记为 A1i 和 A2i 的任务)之间都有状态边,表明它们共享可变的参与者状态。从训练策略到它调用的任务都有控制边缘。要并行训练多个策略,我们可以多次调用 train policy.remote() 。
在同一参与者对象上调用的方法 形成一条由有状态边连接的链( 4)。该链捕获这些方法的调用顺序。有状态边帮助我们将参与者嵌入到无状态任务
中,因为它们捕获共享参与者内部状态的连续方法调用之间的隐式数据依赖关系。有状态的边缘还使我们能够维持血统。与其他数据流系统[64]一样,我们跟踪数据沿袭以实现重建。通过在谱系
中显式包含有状态边,我们可以轻松重建丢失的数据,无论是由远程生成的
函数或参与者方法(👉 4.2.3 节)。
建筑学
Ray 的架构包括 (1) 实现 API 的应用程序层,以及 (2) 提供高可扩展性和容错能力的系统层。应用层
应用层由三类进程组成:- 驱动程序:执行用户程序的进程 。
- Worker:执行由驱动程序或其他人调用的任务
司机
工人
对象存储
本地调度程序
演员
司机
对象存储
本地调度程序
全局调全局 调 节点 节点 全局控制存储 (GCS) 度器 度器节点
工人
工人
对象存储
本地调度程序
度器
全局调
事件日志
功能表
任务表
对象表
错误诊断
分析工具
调试工具
网页用户界 面
系统层(后 端)
应用 层
图 5:Ray 的架构由两部分组成:应用层和系统层。应用层实现👉3节中描述的API和计算模型,系统层实现任务调度和数据管理以满足性能和容错要求。
工•人。 Worker 自动启动并由系统 层分配任务。当声明远程函数时,该函数会自动发布给所有工作人员。工作线程串行执行任务,不维护跨任务的本地状态。Actor:一个有状态的进程,在被调用时仅执行它公开的方法。与工作人员不同,参与者由工作人员或驱动程序显式实例化。与工作人员一样,参与者串行执行方法,不同之处在于每个方法都取决于前一个方法执行所产生的状态。
系统层
系统层由三个主要组件组成:全局控制存储、分布式调度器和分布式对象存储。所有组件都是水平可扩展和容错的。全局控制存储
(GCS) 全局控制存储(GCS)维护系统的整个控制状态,这是我们设计的一个独特功能。 GCS 的核心是具有 pubsub 功能的键值存储。我们使用分片来实现扩展,并使用每个分片链复制[61]来提供容错能力。 GCS及其设计的主要原因是保持每秒可动态生成数百万个任务的系统的容错能力和低延迟。 节点故障时的容错需要一个解决方案来维护沿袭信息。现有的基于谱系的解决方案[64、63、40、28]专注于粗粒度并行性,因此可以使用单个节点(例如主节点、驱动器)来存储谱系而不影响性能。然而,这种设计对于像模拟这样的细粒度和动态工作负载来说是不可扩展的。所以, 我们将持久沿袭存储与其他系统组件分离,允许每个组件独立扩展。保持低延迟需要最大限度地减少任务调度的开销,这涉及选择执行位置,以及随后的任务分派,这涉及从其他节点检索远程输入。许多现有的数据流系统[64,40,48]通过将对象位置和大小存储在集中式调度程序中来耦合这些系统,当调度程序不是瓶颈时,这是一种自然的设计。然而, Ray 目标的规模和粒度要求使集中式调度程序远离关键的任务。小路。让调度程序参与每个对象传输是有利的 对于像 allreduce 这样的分布式训练很重要的原语来说,成本过高,因为它既是通信密集型的,又是延迟敏感的。因此,我们将对象元数据存储在GCS中而不是调度程序中,从而将任务调度与任务调度完全解耦。 总之,GCS 显着简化了 Ray 的整体设计,因为它使系统中的每个组件都成为无状态的。这不仅简化了对容错的支持(即,发生故障时,组件只需重新启动并从 GCS 读取沿袭),而且还可以轻松地独立扩展分布式对象存储和调度程序,因为所有组件通过GCS。另一个好处是可以轻松开发调试、分析和可视化工具。自下而上的分布式调度器
正如👉 2 节中所讨论的,Ray 需要每秒动态调度数百万个任务,这些任务可能只需要几毫秒。我们所知道的集群调度程序都不能满足这些要求。大多数集群计算框架,例如 Spark [64]、CIEL [40] 和 Dryad [28] 都实现了集中式调度程序,它可以提供局部性,但延迟为数十毫秒。分布式调度器,如工作窃取[12]、Sparrow [45]和Canary [47]可以实现高规模,但它们要么不考虑数据局部性[12],要么假设任务属于独立作业[45],要么假设计算图是已知的[47]。 为了满足上述要求,我们设计了一个双层调度器,由全局调度器和每个节点的本地调度器组成。为了避免全局调度程序过载,在节点创建的任务首先提交给节点的本地调度程序。本地调度程序在本地调度任务,除非节点过载(即,其本地任务队列超过预定义阈值),或者无法满足任务的要求(例如,缺少 GPU)。如果本地调度程序决定不在本地调度任务,则会将其转发给全局调度程序。由于该调度程序首先尝试在本地调度任务(即在调度层次结构的叶子处),因此我们将其称为自下而上调度程序。 司机 ... 工人 本地调度 程序 本地调度 程序 全局控 制 状态(GCS)全局调度 器
全局调度 器
工人
工人
工人
工人
节点1 节点N 需要使用 LRU 策略存储到磁盘。
与现有的集群计算框架一样,例如 Spark [64]和 Dryad [28],对象存储仅限于不可变数据。这消除了对复杂一致性协议的需要(因为对象不会更新),并简化了对容错的支持。在节点发生故障的情况下,Ray 通过沿袭重新执行来恢复任何所需的对象。 GCS 中存储的谱系在初始执行期间跟踪无状态任务和有状态参与者;
提交 任务
安排任务 加载
信息
我们使用前者来重建商店中的物 体。
为简单起见,我们的对象存储 不支持dis-
图 6:自下而上的分布式调度程序。任务从驱动程序和工作人员自下而上提交到本地调度程序,并仅在需要时转发到全局调度程序(👉 4.2.2 节)。每个箭头的粗细与其请求率成正比。
全局调度器考虑每个节点的负载和任务的约束来做出调度决策。更准确地说,全局调度程序识别具有任务所请求类型的足够资源的节点集,并且在这些节点中选择提供最低估计等待时间的节点。在给定节点,该时间是 (i) 任务在该节点排队的估计时间(即任务队列大小乘以平均任务执行时间)和
(ii) 任务远程输入的估计传输时间 (即远程输入的总大小除以平均带宽)。全局调度程序通过心跳获取每个节点的队列大小和节点资源可用性,以及来自 GCS 的任务输入的位置及其大小。此外,全局调度程序使用简单的指数平均来计算平均任务执行和平均传输带宽。如果全局调度器成为瓶颈, 我们可以通过 GCS 实例化更多共享相同信息的副本。这使得我们的调度程序架构具有高度可扩展性。
内存中分布式对象存储
为了最大限度地减少任务延迟,我们实现了一个内存分布式存储系统来存储每个任务的输入和输出,或无状态计算。在每个节点上,我们通过共享内存实现对象存储。这允许在同一节点上运行的任务之间进行零拷贝数据共享。作为一种数据格式,我们使用 Apache Arrow [2]。 如果任务的输入不是本地的,则输入会在执行之前复制到本地对象存储。此外,任务将其输出写入本地对象存储。复制消除了由于热数据对象而导致的潜在瓶颈,并最大限度地减少了任务执行时间,因为任务仅从本地内存读取数据/向本地内存写入数 贡献对象,即每个对象都适合单 个节点。像大型矩阵或树这样的分布式对象可以在应用程序级别实现为 future 集合。执行
Ray 是加州大学伯克利分校开发的一个活跃的开源项目† 。 Ray 与 Python 环境完全集成,只需运行 pip install ray 即可轻松安装。该实现包含 40K行代码 (LoC),其中 72% 使用 C++ 实现系统层, 28% 使≈用 Python 实现应用层。 GCS 每个分片使用一个 Redis [50] 键值存储,并且完全是单键操作。 GCS 表按对象和任务 ID 进行分片以进行扩展,并且每个分片都进行链式复制 [61] 以实现容错。我们将本地和全局调度程序实现为事件驱动的单线程进程。在内部,本地调度程序维护本地对象元数据、等待输入的任务以及准备分派给工作人员的任务的缓存状态。为了在不同的对象存储之间传输大对象,我们跨多个 TCP 连接对对象进行条带化。将一切放在一起
图 7 通过一个简单示例说明了 Ray 是如何端到端工作的,该示例将两个对象 a 和 b(可以是标量或矩阵)相加,然后返回结果 c。远程函数 add() 在初始化时自动向 GCS 注册,并分发给系统中的每个工作人员(图 7a 中的步骤 0)。 图 7a 显示了驱动程序调用 add.remote(a, b)触发的分步操作,其中 a 和 b 分别存储在节点 N1和 N2 上。驱动程序将 add(a, b) 提交给本地调度程序(步骤 1),本地调度程序将其转发到全局调度程序(步骤 2)。‡ 接下来,全局调度程序查找 add(a 、b) 的参数存储在 GCS 中(步骤 3),并决定在存储参数 b 的节点 N2 上调度任务(步骤 4)。节点 N2 处的本地调度程序检查本地对象存储是否包含 add(a, b) 的参数(步骤 5)。自从 据。这增加了计算密集型工作负载的吞吐量,这是 许多人工智能应用程序共享的配置文件。为了低延迟,我们将对象完全保留在内存中并将它们驱逐为 †https://github.com/ray-project/ray ‡ 请注意,N1 也可以决定在本地安排任务。 N1 全局控制存储 (GCS) N2
10-1 平均任务延迟 (秒) 10-2 10-3 10-4 10-5 100KB 1MB 10MB 物体尺寸 1.6 位置感知 不感知 百万任务/秒 1.2 0.8 0.4 0.0 10 20 30 40 50 60 100 节点数 (a) 远程执行任务 N1 全局控制存储 (GCS) N2编号
a
N1
- 射线局部调度 (b) 射线可扩 展性 功能 表 工人 @ray.远程定义添加(a,b): 返 a + b 对象 表 1 2 5 4 idcc idaa idbb
本地调度程序
- 射线局部调度 (b) 射线可扩 展性 功能 表 工人 @ray.远程定义添加(a,b): 返 a + b 对象 表 1 2 5 4 idcc idaa idbb
3
本地调度程序
idaa idcc
7
c = ray.get(idc)
@ray.远程定义添加(a,b):
返 a + b
id = add.remote(a, b)
司机
全局调度器
@ray.远程定义
添加(a,
b):返 a + b
司机
@ray.远程定义
添加(a,
b):返 a + b
idc= add.remote(a, b)
功能表
@ray.远程定义
0 添加(a, b):
工人
@ray.远程 定义添加(a,
b):
对象 表
c = ray.get(id )
9
c
8
1
6
对象存 储
7
id 一
a id
b
3
5
ida一个
2
全局调度器
4
本地调度程序
对象存储
8:(a) 任务利用位置感知放置。具 有随机对象依赖性的 1000 个任务被调度到两个节点之一。通过位置感知策略,任务延迟仍然独立于任务输入的大小,而不是增长 1-2 个数量级。 (b)利用GCS和自下而上的分布式调度器
编号 a | N1 |
编号 b | N2 |
编号 c 6 | N2, N1 |
的近线性可扩展性。 Ray 通过 60 个节 点达到每秒 100万个任务的吞吐量。 X ∈ {70、80、}
90 因成本原因省略。
- 返
远程任务的结果
7:添加 a 和 b 并返 c 的端到端示例。实线是数据平面操作,虚线是控制平面操作。 (a) 函数add()由节点1(N1)向GCS注册,在N1上调用,并在N2上执行。 (b) N1 使用 ray.get() 获取 add() 的结果。 c 的对象表条目在步骤 4 中创建,并在 c 复制到 N1 后在步骤 6 中更新。
local store 没有对象 a,它在 GCS 中 查找 a 的位置(步骤 6)。得知 a 存储在 N1 中后,N2 的对象存储将其复制到本地(步骤 7)。由于 add()的所有参数现在都存储在本地,因此本地调度程序在本地工作线程处调用 add()(步骤 8),该工作线程通过共享内存访问参数(步骤 9)。
7b 显示了分别在 N1 处执行 ray.get() 和在 N2 处执行 add() 所触发的分步操作。调用 ray.get(idc 时,驱动程序使用 add() 返
的未来 idc(步骤 1)检查本地对象存储中的值 c。由于本地对象存储不存储 c,因此它会在 GCS 中查找其位置。此时,没有 c 的条目,因为 c 尚未创建。因此,N1 的对象存储向对象表注册一个
调,以便在创建 c 的条目时触发该
调(步骤 2)。同时,在 N2 处,add() 完成执行,将结果 c 存储在本地对象存储中(步骤 3),这又将 c 的条目添加到 GCS
(步骤 4)。结果,GCS 使用 c 的 条目触发对 N1对象存储的调(步骤 5)。接下来,N1从N2复制c
(步骤6),并将c返给ray.get()(步骤7),最终完成任务。
虽然这个例子涉及大量的RPC,
在许多情况下,这个数字要小得 多,因为大多数任务都是在本地调度的,并且 GCS 复由全局和本地调度程序缓存。
评估
在我们的评估中,我们研究以下问题:- Ray 满足👉 2 节中列出的延迟、可扩展性和容错要求的程度如何? (👉 5.1 节)
- 使用 Ray 的 API 编写的分布式原语(例如 allreduce)会产生哪些开销? (👉 5.1 节)
- 在 RL 工作负载的背景下,Ray 与 训练、服务和模拟的专用系统相比如何? (👉 5.2 节)
- 与定制系统相比,Ray 为 RL 应 用提供了哪些优势? (👉 5.3 节)
微基准测试
位置感知任务放置。细粒度负载平衡和位置感知放置是 Ray 中任务的主要优点。一旦放置,参与者就无法将其计算移动到大型远程对象,而任务可以。在 8a 中,在没有数据局部性感知的情况下放置的任务(如执行者方法的情况)在 10-100MB 输入数据大小时会遭受 1-2 个数量级的延迟增加。 Ray通过共享对象存储统一任务和参与者,允许开发人员使用任务对模拟参与者产生的输出进行昂贵的后处理。端到端的可扩展性。的主要好处之一 20000 15000 IOPS 10000 5000 0 16 14 12 10 8 6 4 2 0 1KB 10KB 100KB 1MB 10MB100MB 1GB 物体大小 104 延迟(μs) 103 吞吐量(GB/秒 ) 012345 6789 自开始以来的时 间(秒) 104 写读 节点死亡 103 10 图 9:对象存储写入吞吐量和 IOPS。从单个客户端来看, 16 核实例 (m4.4xlarge) 上的大对象吞吐量超过 15GB/s (红色),小对象吞吐量超过 18K IOPS(青色)。它使用 8 个线程来复制大于 0.5MB 的对象,并使用 1 个线程来复制小对象。条形图报告 1、2、4、8、16 个线程的吞吐量。结果是 5 次运行的平均值。
- 从客户端提交任务的角度来看 GCS 读写延迟的时间线。该链以 2 个副本开始。我们手动触发重新配置如下。 t 4.2s,一个链成员被杀死;紧接着,一个新的≈ 链成员加入,启动状态传输,并将链恢复为双向复制。尽管重新配置,客户端观察到的最大延迟仍低于 30 毫秒。 5000 万个无操作任务 全局控制存储(GCS)和自下而上的分布式调度器能够水平扩展系统以支持细粒度任务的高吞吐量,同时保持容错和低延迟任务调度。在图 8b 中,我们在空任务的极其并行的工作负载上评估了这种能力,增加了 x 轴上的集群大小。我们观察到任务吞吐量逐渐增加的近乎完美的线性。 Ray 在 60 个节点上每秒处理超过 100 万个任务的吞吐量,并继续线性 扩展超过 180 万个任务 8000 GCS 已用内存 (MB) 6000 4000 2000 0 射线,无 GCS 齐 平 射线,GCS 齐平
0 10000 20000 30000 40000 50000 60000 经过时间(秒) 每秒 100 个节点。最右边的数据点显示 Ray 可以在不到一分钟(54 秒)的时间内处理 1 亿个任务,并且变异性最小。正如预期的那样,增加任务持续时间会按平均任务持续时间成比例地降低吞吐量,但总体可扩展性保持线性。虽然由于对象依赖性和应用程序并行性的固有限制,许多实际工作负载可能会表现出更有限的可扩展性,但这证明了我们的整体架构在高负载下的可扩展性。 对象存储性能。为了评估对象存储的性能(👉 4.2.3 节),我们跟踪两个指标:IOPS(对于小对 象)和写入吞吐量(对于大对象)。在图 9 中,随着对象大小的增加,单个客户端的写入吞吐量超过 15GB/s。对于较大的对象,memcpy 主导对象创建时间。对于较小的对象,主要开销在于客户端和对象存储之间的序列化和 IPC。 GCS 容错。为了保持低延迟,同时提 供强一致性和容错能力,我们在 Redis 之上构建了一个轻量级的链复制 [61] 层。图 10a 模拟将 Ray 任务记录到 GCS 以及从 GCS 读取任务,其中键为 25 字节,值为 512 字节。客户端尽可能快地发送请求,一次最多有一个正在进行的请求。失败会从客户端报告给链主(已收到明确的错误,或尽管重试但仍超时)或
- Ray GCS 通过 GCS 刷新保持恒定的内存占用。如果没有 GCS 刷新,内存占用量将达到最大容量,并且工作负载无法在预定持续时间内完成(由红叉表示)。 图 10:Ray GCS 容错和刷新。 来自链中的任何服务器(已收到明确的错误)。总体而言,重新配置导致客户端观察到的最大延迟低于 30 毫秒(这包括故障检测和恢复延迟)。 GCS 冲洗。 Ray 可以定期将 GCS 的 内容刷新到磁盘。在图 10b 中,我们按顺序提交 5000 万个空任务并监控 GCS 内存消耗。正如预期的那样,它随着跟踪的任务数量线性增长,并最终达到系统的内存容量。此时,系统将陷入停滞,并且工作负载无法在合理的时间内完成。通过定期 GCS 刷新,我们实现了两个目标。首先,内存占用量被限制在用户可配置的级别(在微基准测试中,我们采用积极的策略,将消耗的内存保持在尽可能低的水平)。其次,刷新机制提供了一种将长期运行的 Ray 应用程序的沿袭快照快照到磁盘的自然方法。 从任务失败中恢复。在图 11a 中, 我们 吞吐量(任务/秒) 2000 1500 1000 500 60 104 原来的任务 重新执行的任务 103 迭代时间( 毫秒) 40 102 节点数 量 101 20 100 10兆字节 100兆字节 1兆字节 800 OpenMPI Ray* 射线 700 600 500 迭代时间( 毫秒) 400 300 200 100 0 Ray Ring减少延迟(16个 节点,100MB) +0 +1 +5 +10 00 0 50 100 150 200 自开始以来的时间 (秒) 物体尺寸
- Ray 与 OpenMPI 添加了调度程序延迟(毫秒
- 射线调度器消融
- 任务重构 原来的任务 重新执行的任 务 检查点任务 100 200 300 400 500 600 自开始以来的时间(秒)
12:(a) allreduce 在 16 个 m4.16xl 节点上的平均执行时间。每个工作线程都在不同的节点上运行。 Ray* 将 Ray 限制为 1 个发送线程和 1 个接收线程。
- Ray 的低延迟调度对于 allreduce 至关重 要。
11:射线容错。 (a) Ray 在节点被删除时重建丢失的任务依赖关系(虚线),并在节点添加回来时恢复到原始吞吐量。每个任务的时间为 100 毫秒,并且依赖于先前提交的任务生成的对象。 (b) 参与者是从最后一个检查点重建的。在 t = 200 秒时,我们杀死 10 个节点中的 2 个,导致集群中 2000 个参与者中的 400 个在剩余节点上恢复(t = 200-270 秒)。 展示 Ray 使用持久的 GCS 沿袭存储从工作节点故障中透明恢复和弹性扩展的能力。在 m4.xlarge 实例上运行的工作负载由驱动程序提交的 100ms 任务的线性链组成。当节点被删除时(在 25 秒、50 秒、 100 秒),本地调度程序会重建链中先前的结果以继续执行。每个节点的总体吞吐量始终保持稳定。 从演员失败中恢复过来。通过直接在依赖
中将参与者方法调用编码为有状态边,我们可以重用与
11a 中相同的对象重建机制,为有状态计算提供透明的容错能力。 Ray还利用用户定义的检查点函数来限制参与者的重建时间(
11b)。在开销最小的情况下,检查点仅允许重新执行 500 个方法,而没有检查点则可以重新执行 10k 个方法。未来,我们希望进一步减少 Actor 重建时间,例如,允许用户注释不改变状态的方法。 全部减少。 Allreduce是一种分布式通 信 MPI 实×现× OpenMPI (v1.10),
分别增加了 1.5 和 2( 12a)。 我们将 Ray 的性能归因于它使用多个线程进行网络传输,充分利用了 AWS 上节点之间的 25Gbps 连接,而 OpenMPI在单个线程上顺序发送和接收数据 [22]。对于较小的对象,OpenMPI 通过切换到开销较低的算法而优于 Ray,这是我们计划在未来实现的优化。 Ray 的调度程序性能对于实现 allreduce 等原语至关重要。在
12b 中,我们注入了人工任务执行延迟,结果表明,仅几毫秒的额外延迟,性能就下 降了近 2。具×有集中式调度程序(例如 Spark 和 CIEL)的系统通常具有数十毫秒的调度程序开销 [62, 38],使得此类工作负载不切实际。调度程序吞吐量也成为瓶颈,因为环减少所需的任务数量与参与者数量呈二次方关系。
- 从客户端提交任务的角度来看 GCS 读写延迟的时间线。该链以 2 个副本开始。我们手动触发重新配置如下。 t 4.2s,一个链成员被杀死;紧接着,一个新的≈ 链成员加入,启动状态传输,并将链恢复为双向复制。尽管重新配置,客户端观察到的最大延迟仍低于 30 毫秒。 5000 万个无操作任务 全局控制存储(GCS)和自下而上的分布式调度器能够水平扩展系统以支持细粒度任务的高吞吐量,同时保持容错和低延迟任务调度。在图 8b 中,我们在空任务的极其并行的工作负载上评估了这种能力,增加了 x 轴上的集群大小。我们观察到任务吞吐量逐渐增加的近乎完美的线性。 Ray 在 60 个节点上每秒处理超过 100 万个任务的吞吐量,并继续线性 扩展超过 180 万个任务 8000 GCS 已用内存 (MB) 6000 4000 2000 0 射线,无 GCS 齐 平 射线,GCS 齐平
建筑模块
端到端应用(例如 AlphaGo [54])需要训练、服务和模拟的紧密耦合。在本节中,我们将每个工作负载隔离到一个设置中,以说明典型的 RL 应用程序的要求。由于针对 RL 的灵活编程模型以及旨在支持该编程模型的系统,Ray 可以匹配甚至有时超过这些单独工作负载的专用系统的性能。 7000 平均图像 数/秒 6000 5000 4000 3000 2000 1000 0 Horovod + TF分布式 TF Ray + TF4 8 16 32 64 GPU 数量 (V100)
表 3:专用服务系统 Clipper [19] 和两个 嵌入式服务工作负载的 Ray 的吞吐量比较。 图 13:分发 ResNet-101 TensorFlow 模型训 练时每秒达到的图像数(来自官方 TF 基准)。所有实验都在通过 25Gbps 以太网连接的 p3.16xl 实例上运行,工作人员为每个节点分配 4 个 GPU,如 Horovod [53] 中所做的那样。我们注意到与之前报告的一些测量偏差,可能是由于硬件差异和最近的 TensorFlow 性能改进所致。我们在所有运行中都使用 OpenMPI 3.0、TF 1.8 和 NCCL2。系统
小输入
更大的输入
快艇
4400 ± 15 状态/秒
290 ± 1.3 状态/秒
射线
6200 ± 21 状态/秒
6900 ± 150 状态/秒
分布式训练
我们利用 Ray actor 抽象来表示模型副本,实现数据并行同步 SGD。模型权重通过 allreduce (5.1)或参数服务器同步,两者都在 Ray API 之上实现。在图 13 中,我们针对最先进的实现 [53] 评估了 Ray(同步)参数服务器 SGD 实现的性能,每个实验使用相同的 TensorFlow 模型和合成数据生成器。我们仅与基于 TensorFlow 的系统进行比较,以准确测量 Ray 带来的开销,而不是深度学习框架本身之间的差异。在每次迭代中,模型副本参与者并行计算梯度,将梯度发送到分片参数服务器,然后从参数服务器读取梯度总和以进行下一次迭代 迭代。 图 13 显示 Ray 与 Horovod 的性能相匹配,并且与分布式 TensorFlow(分布式复制模式)的差距在 10% 以内。这是因为能够在 Ray 的通用 API 中表达这些专用系统中相同的应用程序级优化。一个关键的优化是单次迭代中梯度计算、传输和求和的流水线。为了将 GPU 计算与网络传输重叠,我们使用自定义 TensorFlow 运算符将张量直接写入 Ray的对象存储。服务
模型服务是端到端应用程序的重要组成部分。 Ray主要关注在同一动态任务图中(例如,Ray 上的 RL应用程序内)运行的模拟器的模型嵌入式服务。相比之下,像 Clipper [19] 这样的系统专注于向外部客户端提供预测。 在这种情况下,低延迟对于实 现高利用率至关重要。为了证明这一点,我们在表 3 中比较了 我们使用残差网络和小型全连接网络,分别花费 10ms 和 5ms 进行评估。服务器由客户端查询,每个客户端以 64为一组分别发送大小为 4KB 和 100KB 的状态。 与使用 REST 上的开源 Clipper 系统相比,使用 Ray actor 来服务策略所实现的服务器吞吐量。在这里,客户端和服务器进程都位于同一台计算机 (p3.8xlarge 实例)上。对于 RL 应用 程序来说通常是这种情况,但对于 Clipper 等系统处理的一般 Web 服务工作负载则不然。由于其低开销序列化和共享内存抽象,Ray 为小型全连接策略模型实现了高一个数量级的吞吐量,该模型接受大量输入,并且在更昂贵的残差网络策略模型上速度也更快,类似于使用的模型在 AlphaGo Zero 中,需要较小的输入。模拟
强化学习中使用的模拟器会产生可变长度(“时间步长”)的结果,由于训练的紧密循环,必须在可用时立即使用。任务的异构性和及时性要求使得仿真很难在 BSP 类型的系统中有效支持。为了进行演示,我们将 (1) 一个在 3 轮中在 n 个核心上提交 3n个并行模拟运行的 MPI 实现(在轮次之间存在全局屏障§)与 (2) 一个同时发出相同 3n 任务的 Ray程序进行比较。正在将模拟结果收集回给驾驶员。表 4 显示两个系统都具有良好的扩展性,但 Ray 的吞吐量高达 1.8。这激发了一种可以动态生成和收集细粒×度模拟任务结果的编程模型。
表 4:OpenAI Gym [13] 中 Pendulum-v0 模拟器的每秒时间步长。 Ray 在大规模运行异构模拟时可以实现更好的利用率。 §请注意,专家可以使用 MPI 的异步原语来绕过障碍,但代价是增加程序复杂性,尽管如此,我们还是选择了这样的实现来模拟 BSP。系统、编程模型
1个CPU
16个CPU
256 个 CPU
MPI,批量同步
22.6K
208K
2.16M
Ray,异步任务
22.3K
290K
4.03M
强化学习应用
如果没有一个可以紧密耦合训练、模拟和服务步骤的系统,如今的强化学习算法都是作为一次性解决方案来实现的,这使得很难整合优化,例如需要不同的计算结构或利用不同的架构。因此,通过实施两种代表性的强化学习 90 参考ES 雷·ES x x x 80 70 60 平均解决时间 (分钟 50 40 30 20 10 0 256 1024 8192 CPU 数量 500 MPI PPO(英 语:MPI PPO) 雷·PPO 400 平均解决时间(分钟 300 200 100 0 8x1 64x8 512x64 CPU x GPU Ray 中的应用程序,我们能够匹配甚至超越专门为这些算法构建的定制系统 节奏。主要原因是 Ray 编程模型的灵活性,它可以表达应用程序级优化,这需要大量的工程工作才能移植到定制系统,但 Ray 的动态任务图执行引擎透明地支持。进化策略
为了在大规模 RL 工作负载上评估 Ray,我们实现了进化策略 (ES) 算法,并与参考实现 [49] 进行比较—— 一个专门为此算法构建的系统,依赖于 Redis 进行消息传递,并依赖于低级多处理库进行数据共享。该算法定期向工作人员池广播新策略,并汇总大约 10000 个任务的结果(每个任务执行 10 到 1000 个模拟步骤)。 如图 14a 所示,Ray 上的实现可扩 展到 8192 个核心。将可用内核数量增加一倍可使平均完成时间 加速 1.6 倍。相反,专用系统无法在×2048 个核心 上完成,系统中的工作超出了应 用程序驱动程序的处理能力。为了避免这个问题,Ray 实现使用了参与者的聚合树,平均时间达到 3.7 分钟,比最佳已发布结果(10 分钟)快两倍多。 使用 Ray 进行串行实现的初始并行化仅需要修改 7 行代码。借助 Ray 对嵌套任务和参与者的支持,可以轻松实现通过分层聚合来提高性能。相比之下,参考实现有数百行代码专用于工作人员之间通信任务和数据的协议,并且需要进一步的工程来支持分层聚合等优化。近端策略优化
我们在 Ray 中实现近端策略优化 (PPO) [51],并与使用 OpenMPI 通信原语的高度优化的参考实现 [5] 进行比较。该算法是一种异步分散收集,其中新任务在模拟参与者执行任务时被分配给他们 (a) 进化策略 (b) 聚苯醚 图 14 : 在 Humanoidv1 任务中达到 6000 分 的时间 [13]。 (a) Ray ES 实现可很好地扩展到 8192 个核心,并实现 3.7 分钟的中位时间,是已发布的最佳结果的两 倍多。专用系统无法运行超过 1024 个内核。在此基准测试中,ES 比 PPO 更快,但显示出更大的运行时差异。 (b) Ray PPO 实现的性能优于 GPU 较少的专用 MPI 实现 [5],而成本仅为其一小部分。 MPI 实现需要每 8 个 CPU 1 个 GPU,而 Ray 版本最多需要 8 个 GPU(每 8个 CPU 永远不会超过 1 个 GPU)。 将卷轴返回给驱动程序。提交任务直至收集到 320000 个模拟步骤(每个任务产生 10 到 1000 个步骤)。策略更新执行 20 步 SGD,批量大小为 32768。本示例中的模型参数约为 350KB。这些实验是使用 p2.16xlarge(GPU )和 m4.16xlarge (高 CPU)实例运行的。 如图 14b 所示,Ray 实现在所有实验中均优于优化的 MPI 实现,同时仅使用一小部分 GPU。原因是 Ray 具有异构性感知能力,允许用户通过以任务或参与者的粒度表达资源需求来利用非对称架构。然后, Ray 实现可以利用 TensorFlow 的单进程多 GPU 支持,并在可能的情况下将对象固定在 GPU 内存中。由于需要异步收集部署到单个 GPU 进程,因此这种优化无法轻松移植到 MPI。事实上,[5] 包括两种 PPO 的自定义实现,一种针对大型集群使用 MPI,另一种针对 GPU 进行了优化,但仅限于单个节点。 Ray 允许适合这两种情况的实现。 Ray 处理资源异构性的能力还将 PPO 的成本降低了 4.5 倍 [4],因为仅 CPU 任务可以调度在更便宜的高 CPU 实例上。相比之下,MPI 应用程序通常呈现对称架构,其中所有进程都运行相同的代码并需要相同的资源,在这种情况下,会阻止使用仅包含 CPU 的计算机进行横向扩展。此外,MPI 实现需要按需实例,因为它不能透明地处理故障。假设现货实例便宜 4 倍,Ray 的容错能力和资源感知调度一起可将成本降低 18 倍。
相关工作
动态任务。 Ray 与 CIEL [40] 和 Dask [48]密切相关。所有这三个都支持具有嵌套任务的动态任务
并实现 future 抽象。 CIEL 还提供基于谱系的容错,而 Dask 与 Ray 一样,与 Python 完全集成。然而,Ray 在两个方面有所不同,这两个方面会产生重要的性能影响。首先,Ray 使用参与者抽象扩展了任务模型。这对于分布式训练和服务中的高效状态计算是必要的,以保持模型数据与计算并置。其次,Ray 采用完全分布式和解耦的控制平面和调度程序,而不是依赖于存储所有元数据的单个主机。这对于在不修改系统的情况下有效支持像 allreduce 这样的原语至关重要。在 16 个节点上 100MB 的峰值性能下, Ray 上的 allreduce ( 👉 5.1 节)在 200 毫秒内提交 32 轮,每轮 16 个任务。同时, Dask 报告在 512 个核心上的最大调度程序吞吐量为 3k 任务/秒 [3]。使用集中式调度程序,每轮 allreduce 都会产生至少 5 毫秒的调度延迟,最多会导致更差的完成时∼间(
12b)。即 使使用分散式调度程序,×将控制平面信息与调度程 序相结合也会使调度程序处于数据传输的关键路径上,从而为每一轮 allreduce 增加额外的往返。 数据流系统。 流行的数据流系统, 例如 MapReduce [20]、Spark [65] 和 Dryad [28] 已广泛应用于分析和 ML 工作负载,但它们的计算模型对于细粒度和动态模拟工作负载来说限制太多。 Spark和MapReduce实现了BSP执行模型,该模型假设同一阶段内的任务执行相同的计算并花费大致相同的时间。 Dryad 放宽了这一限制,但缺乏对动态任务
的支持。此外,这些系统都没有提供参与者抽象,也没有实现分布式可扩展控制平面和调度程序。最后,Naiad [39] 是一个数据流系统,可为某些工作负载提供改进的可扩展性,但仅支持静态任务
。 机器学习框架。 TensorFlow [7] 和 MXNet [18]针对深度学习工作负载,并有效地利用 CPU 和 GPU。虽然它们在由线性代数运算的静态 DAG 组成的训练工作负载方面取得了出色的性能,但它们对将训练与模拟和嵌入式服务紧密耦合所需的更通用计算的支持有限。 TensorFlow Fold [33] 通过其内部 C++ API 为动态任务
以及 MXNet 提供了一些支持,但两者都不完全支持在执行期间修改 DAG 以响应任务进度、任务完成时间或故障的能力。 TensorFlow和 MXNet 原则上通过允许程序实现通用性: mer 来模拟低级消息传递和同步原语,但这种情况下的陷阱和用户体验与 MPI 类似。 OpenMPI [22]可以实现高性能,但编程相对困难,因为它需要显式协调来处理异构和动态任务
。此外,它还迫使程序员显式地处理容错问题。 演员系统。 Orleans [14] 和 Akka [1] 是 两个 Actor 框架,非常适合开发高可用和并发的分布式系统。然而,与 Ray 相比,它们对数据丢失恢复的支持较少。要恢复有状态的 Actor,Orleans 开发人员必须显式检查 Actor 状态和中间响应。 Orleans 中的无状态 Actor 可以进行复制以进行横向扩展,因此可以充当任务,但与 Ray 不同,它们没有血统。类似地,虽然 Akka 明确支持跨故障持久保存 Actor 状态,但它不为无状态计算(即任务)提供有效的容错能力。对于消息传递,Orleans 提供 at-least-once 语义,Akka 提供 at-most-once语义。相比之下,Ray 提供透明的容错和一次性语义,因为每个方法调用都记录在 GCS 中,并且参数和结果都是不可变的。 我们发现实际上这些限制不会影响我们应用程序的性能。 Erlang [10] 和 C++ Actor Framework [17],另外两个基于 Actor 的系统,对容错的支持也同样有限。 全局控制存储和调度。逻辑上集中控制平面的概念先前已在软件定义网络(SDN)[16]、分布式文件系统(例如 GFS [23])、资源管理(例如 Omega [52] ) 和分布式框架( 例如, MapReduce [20] 、 BOOM [9])等等。雷从这些开创性的努力中汲取灵感,但也做出了重大改进。与 SDN、BOOM 和 GFS相比,Ray 将控制平面信息(例如 GCS)的存储与逻辑实现(例如调度程序)解耦。这允许存储层和计算层独立扩展,这是实现我们的可扩展性目标的关键。 Omega 使用分布式架构,其中调度程序通过全局共享状态进行协调。在此架构中,Ray 添加了全局调度程序来平衡本地调度程序之间的负载,并针对毫秒级而不是秒级任务调度。 Ray 实现了独特的分布式自下而上调度程序,该调度程序可水平扩展,并且可以处理动态构建的任务
。与 Ray 不同,大多数现有的集群计算系统 [20,64,40] 使用集中式调度器架构。虽然 Sparrow [45] 是去中心化的,但它的调度器做出独立的决策,限制了可能的调度策略,并且作业的所有任务都由同一个全局调度器处理。 Mesos [26] 实现了一个两级分层调度程序,但其顶层调度程序管理框架,而不是单个任务。 Canary [47] 通过让每个调度程序实 例处理任务
的一部分,但不处理动态计算
,实现了令人印象深刻的性能。 Cilk [12] 是一种并行编程语言,其工作窃取调度程序可实现动态任务
的高效负载平衡。然而,由于没有像 Ray 的全局调度器这样的中央协调器,这种完全并行的设计也很难扩展以支持分布式环境中的数据局部性和资源异构性。
讨论和经验
构建 Ray 是一个漫长的旅程。它 于两年前开始使用 Spark 库来执行分布式训练和模拟。然而,BSP 模型相对不灵活、每个任务的开销较高以及缺乏参与者抽象,导致我们开发了一个新系统。自从我们大约一年前发布 Ray 以来,已有数百人使用过它,并且有几家公司正在生产中运行它。在这里我们讨论我们开发和使用 Ray 的经验,以及一些早期的用户反馈。 API。在设计API时,我们强调极简主义。最初我们从基本的任务抽象开始。后来, 我们添加了 wait() 原语以适应具有异构持续时间的推出,并添加了 actor 抽象以适应👉三方模拟器并分摊昂贵的初始化的开销。虽然生成的 API 级别相对较低,但事实证明它功能强大且易于使用。我们已经使用这个 API 在 Ray 之上实现了许多最先进的 RL 算法,包括 A3C [36]、PPO [51]、DQN [37]、ES [49]、 DDPG [55] 和 Ape-X [27]。在大多数情况下,我们只需几十行代码即可将这些算法移植到 Ray。根据早期用户反馈,我们正在考虑增强 API 以包含更高级别的原语和库,这也可以为调度决策提供信息。局限性。考虑到工作负载的普遍性,专门的优化是很困难的。例如,我们必须在不完全了解计算的情况下做出调度决策。 Ray 中的调度优化可能需要更复杂的运行时分析。此外,存储每个任务的沿袭需要实施垃圾收集策略来限制存储成本 GCS,我们正在积极开发的一项功能。 容错性。我们经常被问到人工智能应用程序是否真的需要容错。毕竟,由于许多人工智能算法的统计性质,人们可以简单地忽略失败的推出。根据我们的经验,我们的答案是“是”。首先,忽略故障的能力使应用程序更容易编写和推理。其次,我们通过确定性重放实现的容错功能极大地简化了调试,因为它使我们能够轻松地重现大多数错误。这一点尤其重要,因为由于其随机性,人工智能总是 众所周知,算法很难调试。👉三 ,容错有助于节省资金,因为它允许我们在 AWS 上的现货实例等廉价资源上运行。当然,这是以一些开销为代价的。然而,我们发现对于我们的目标工作负载来说,这种开销是最小的。 GCS 和水平可扩展性。 GCS 极大地简化了 Ray开发和调试。它使我们能够在调试 Ray 本身时查询整个系统状态,而不必手动公开内部组件状态。此外,GCS 也是我们时间轴可视化工具的后端,用于应用程序级调试。 GCS 对 Ray 的水平可扩展性也发挥了重要作用。在👉 5 节中,每当 GCS 成为瓶颈时,我们就可以通过添加更多分片来进行扩展。 GCS 还使全局调度程序能够通过简单地添加更多副本来扩展。由于这些优点,我们相信集中控制状态将成为未来分布式系统的关键设计组成部分。
结论
当今没有通用系统能够有效支持 训练、服务和模拟的紧密循环。为了表达这些核心构建块并满足新兴人工智能应用的需求,Ray 将任务并行和参与者编程模型统一在单个动态任务中,并采用由全局控制存储和自下而上的分布式调度程序支持的可扩展架构。该架构同时实现的编程灵活性、高吞吐量和低延迟对于新兴的人工智能工作负载尤其重要,这些工作负载产生的任务在资源要求、持续时间和功能方面各不相同。我们的评估展示了高达每秒 180万个任务的线性可扩展性、透明的容错能力以及对多个当代 RL 工作负载的显着性能改进。因此,Ray提供了灵活性、性能、 以及未来人工智能应用开发的易用性。
致谢
这项研究得到了 NSF CISE Expeditions Award CCF-1730628 的部分支持,以及来自阿里巴巴、亚马逊网络服务、蚂蚁金服、Arm、CapitalOne、爱立信、Facebook、谷歌、华为、英特尔、微软、丰业银行、Splunk 和 VMware 的捐赠以及由 NSF 授予 DGE-1106400。我们感谢匿名审稿人和我们的牧羊人米格尔·卡斯特罗(Miguel Castro)深思熟虑的反馈,这有助于提高本文的质量。
参考文献
- 阿卡。https://akka.io/.
- 阿帕奇之箭。https://arrow.apache.org/.
- Dask 基准。 http://matthewrocklin.com/blog/ 工作/2017/07/03/缩放.
- EC2 实例定价。 https://aws.amazon.com/ec2/ 定价/按需/.
- OpenAI Baselines : 强化学习算法的高质量实现。 https://github.com/openai/ 基线.
- TensorFlow 服务。 https://www.tensorflow.org/ 服务/.
- 阿巴迪,M.,巴勒姆,P.,陈,J.,陈 ,Z.,戴维斯,A.,迪恩,J.,德文,M.,格玛瓦特,S.,欧文,G.,伊萨德, M.,等人。 TensorFlow:大规模机器学习系统。👉 12 届 USENIX 操作系统设计与实现 (OSDI) 研讨会论文集。美国乔治亚州萨凡纳 (2016)。
- 阿加瓦尔,A.,伯德,S.,科佐维奇,M.,黄,L.,兰福德, J.,李,S.,李,J.,梅拉梅德,D.,奥什里,G.,里巴斯, O., SEN, S. 和 SLIVKINS, A. 多世界测试决策服务。 arXiv 预印本 arXiv:1606.03966 (2016)。
- Alvaro, P.、CONDIE, T.、CONWAY, N.、ELMELEEGY, K.、 HELLERSTEIN, J. M. 和 SEARS, R. BOOM Analytics:探 索 以数据为中心的云声明式编程。👉五届欧洲计算机系统会议记录 (2010),ACM,👉 223-236 页。
- 阿姆斯特朗,J.,维丁,R.,维克斯特罗姆,C.,和 WILLIAMS, M. ERLANG 中的并发编程。
- BEATTIE, C.、LEIBO, J. Z.、TEPLYASHIN, D.、WARD, T.、 WAINWRIGHT, M.、KU ́TTLER, H.、LEFRANCQ, A.、GREEN, S.、VALDE ́S, V.、SADIK, A.,等人。深度思 维实验室。 arXiv 预印本 arXiv:1612.03801 (2016)。
- BLUMOFE, R. D. 和 LEISERSON, C. E. 通过工作窃取来调 度多线程计算。 J. ACM 46, 5(1999 年 9 月 ),720–748。
- BROCKMAN, G.、CHEUNG, V.、PETTERSSON, L.、SCHNEIDER, J.、SCHULMAN, J.、TANG, J. 和 ZAREMBA, W. OpenAI 健 身房。 arXiv 预印本 arXiv:1606.01540 (2016)。
- BYKOV, S. 、GELLER, A. 、KLIOT, G. 、LARUS, J. R. 、 PANDYA, R. 和 THELIN, J. Orleans:面向所有人的云计算。 👉二届 ACM 云计算研讨会论文集 (2011),ACM,👉 12 页。 16.
- CARBONE, P. 、 EWEN, S. 、 FO ́ RA, G. 、 HARIDI, S. 、 RICHTER, S. 和 TZOUMAS, K. Apache Flink 中的状态管理:一致的有状态分布式流处理。过程。 VLDB 捐赠。 10、12 (2017 年 8 月),1718–1729。
- CASADO, M.、FREEDMAN, M. J.、PETTIT, J.、LUO, J.、 MCKEOWN, N. 和 SHENKER, S. Ethane : 控制 企 业。 SIGCOMM 计算。交流。 Rev. 37, 4(2007 年 8 月),1– 12。
- CHAROUSSET, D.、SCHMIDT, T. C.、HIESGEN, R. 和 WA ́ HLISCH, M. 原生参与者:适用于分布式异构环境的可扩展软件平台。 2013 年基于参与者、代理和分散控制的编程研讨会论文集 (2013),ACM,👉 87-96 页。
- 陈涛、李明、李勇、林明、王娜、王明、肖涛、徐斌、张成、张志。 MXNet:一个灵活高效的异构分布式系统机器学习库。在关于机器学习系统的 NIPS 研讨会 (LearningSys’16) (2016)。
- CRANKSHAW, D.、WANG, X.、ZHOU, G.、FRANKLIN, M. J.、 GONZALEZ, J. E. 和 STOICA, I. Clipper:低延迟在线预测服务系统。👉 14 届 USENIX 网络系统设计与实现研讨会 (NSDI 17)(马萨诸塞州波士顿,2017 年),USENIX 协会, 👉 613-627 页。
- DEAN, J. 和 GHEMAWAT, S. MapReduce:大型集群上的简化数据处理。交流。 ACM 51, 1(2008 年 1 月),107–113。
- Dennis, J. B. 和 MISUNAS, D. P. 基本数据流处理 器的初步架构。👉二届计算机体系结构年度研讨会论文集(美国纽约州纽约市,1975 年),ISCA ’75,ACM,👉 126-132 页。
- 加布里埃尔,E.,FAGG,G.E.,博西尔卡,G.,安格斯昆, T.,东加拉,J.J.,斯奎尔斯,J.M.,萨哈伊,V.,坎巴杜尔,P.,巴雷特,B.,卢姆斯丹,A.,卡斯坦,R.H. 、 DANIEL, D. J.、GRAHAM, R. L. 和 WOODALL, T. S. 开放 MPI:下一代 MPI 实现的目标、概念和设计。会议记录,👉 11 届欧洲 PVM/MPI 用户组会议(匈牙利 布达佩斯,2004年 9 月),👉 97-104 页。
- GHEMAWAT, S.、Gobioff, H. 和 LEUNG, S.-T.谷歌文 件系统。 29-43。
- GONZALEZ, J. E.、XIN, R. S.、DAVE, A.、CRANKSHAW, D.、 FRANKLIN, M. J. 和 STOICA, I. GraphX:分布式数据流框架中的图形处理。👉 11 届 USENIX 操作系统设计与实现会议(美国加利福尼亚州伯克利,2014 年)论文集,OSDI’14, USENIX 协会,👉 599-613 页。
- GU*, S., HOLLY*, E., LILLICRAP, T., AND LEVINE, S. 用于具有异步离策略更新的机器人操 作的深度强化学习。 IEEE 国际机器人与自动化会议 (ICRA 2017) (2017)。
- HINDMAN, B.、KONWINSKI, A.、ZAHARIA, M.、GHODSI, A.、 JOSEPH, A. D.、KATZ, R.、SHENKER, S. 和 STOICA, I. Mesos:细粒度资源共享平台数据中心。👉八届 USENIX 网络系统设计与实现会议(美国加利福尼亚州伯克利,2011年)论文集,NSDI’11,USENIX 协会,👉 295-308 页。
- HORGAN, D.、QUAN, J.、BUDDEN, D.、BARTH-MARON, G.、 HESSEL, M.、VAN HASSELT, H. 和 SILVER, D. 分布式优先 体验重播。国际学习表征会议(2018)。
- ISARD, M.、BUDIU, M.、YU, Y.、BIRRELL, A. 和 FETTERLY, D. Dryad:来自连续构建块的分布式数据并行程序。 2007年👉二届 ACM SIGOPS/EuroSys 欧洲计算机系统会议论文集 (美国纽约州纽约市,2007 年),EuroSys '07,ACM,👉 59-72 页。
- JIA, Y., SHELHAMER, E., DONAHUE, J., KARAYEV, S., LONG, J., GIRSHICK, R., GUADARRAMA, S., AND DARRELL, T. Caffe:用于快速特征嵌入的卷积架构。 arXiv 预印本 arXiv:1408.5093 (2014)。
- JORDAN, M. I. 和 MITCHELL, T. M. 机器学习:趋势、观点和前景。科学 349, 6245 (2015), 255–260。
- LEIBIUSKY, J.、EISBRUCH, G. 和 SIMONASSI, D. Storm 入门。奥莱利媒体公司,2012 年。
- LI, M.、ANDERSEN, D. G.、PARK, J. W.、SMOLA, A. J.、 AHMED, A.、JOSIFOVSKI, V.、LONG, J.、Shekita, E. J. 和 SU, B.-Y。使用参数服务器扩展分布式机器学习。👉 11届 USENIX 操作系统设计与实现会议论文集(美国加利福尼亚州伯克利,2014 年),OSDI’14,👉 583-598 页。
- 看起来,M.,赫雷肖夫,M.,哈钦斯,D.,和诺维格 , P. 具有动态计算
的深度学习。 arXiv 预印本 arXiv:1702.02181 (2017)。
- LOW, Y. 、GONZALEZ, J. 、KYROLA, A. 、BICKSON, D. 、 GUESTRIN, C. 和 Hellerstein, J. GraphLab:并行机 器学习的新框架。载于👉二十六届人工智能不确定性会议论文集 (美国弗吉尼亚州阿灵顿,2010 年),UAI’10,👉 340- 349 页。
- MALEWICZ, G.、AUSTERN, M. H.、BIK, A. J.、DEHNERT, J. C.、HORN, I.、LEISER, N. 和 CZAJKOWSKI, G. Pregel: 大规模
形处理系统。 2010 年 ACM SIGMOD 国 际数据管理会议论文集(美国纽约州纽约市,2010 年),SIGMOD '10, ACM,👉 135-146 页。
- MNIH,V.,巴迪亚,A.P.,米尔扎,M.,格雷夫斯,A., LILLICRAP, T. P.、HARLEY, T.、SILVER, D. 和 KAVUKCUOGLU, K. 深 度强化学习的异步方法。国际机器学习会议(2016)。
- MNIH, V.、KAVUKCUOGLU, K.、SILVER, D.、RUSU, A. A.、 VENESS, J.、BELLEMARE, M. G.、GRAVES, A.、RIEDMILLER, M.、FIDJELAND, A. K.、OSTROVSKI, G. 等。通过深度强化 学习实现人类水平的控制。自然 518, 7540 (2015), 529– 533。
- MURRAY, D. 支持数据相关控制流的分布式执行引擎。剑桥大学,2012。
- MURRAY, D. G.、MCSHERRY, F.、ISAACS, R.、ISARD, M.、 BARHAM, P. 和 ABADI, M. Naiad:及时数据流系统。👉二十四届 ACM 操作系统原理研讨会论文集(美国纽约州纽约市,2013 年),SOSP ’13,ACM,👉 439-455 页。
- MURRAY, D. G.、SCHWARZKOPF, M.、SMOWTON, C.、SMITH, S.、MADHAVAPEDDY, A. 和 HAND, S. CIEL:分布式数据流 计算的通用执行引擎。👉八届 USENIX 网络系统设计与实现会议记录(美国加利福尼亚州伯克利,2011 年),NSDI’11, USENIX 协会,👉 113-126 页。
- NAIR, A.、SRINIVASAN, P.、BLACKWELL, S.、ALCICEK, C.、 FEARON, R. 、 MARIA, A.D. 、 PANNEERSHELVAM, V. 、 SULEYMAN, M.、BEATTIE, C.、PETERSEN, S.、LEGG ,S., MNIH,V.,KAVUKCUOGLU,K.,和 SILVER,D。深度强化学习的大规模并行方法,2015 年。
- NG, A.、COATES, A.、DIEL, M.、GANAPATHI, V.、SCHULTE, J.、TSE, B.、BERGER, E. 和 LIANG, E. 通过强化学习实 现自主倒转直升机飞行。实验机器人九 (2006), 363–372。
- NISHIHARA, R.、MORITZ, P.、WANG, S.、TUMANOV, A.、 PAUL, W.、SCHLEIER-SMITH, J.、LIAW, R.、NIKNAMI, M.、 JORDAN, M. I. 和 STOICA, I . 实时机器学习:缺失的部分。操作系统热门话题研讨会 (2017)。
- OPENAI 。 OpenAI Dota 2 1v1 机 器 人 。 https://openai.com/ 国际/, 2017.
- 奥斯特豪特,K.,温德尔,P.,扎哈里亚,M.,和斯托 卡, I. Sparrow:分布式、低延迟调度。👉二十四届 ACM 操作系统原理研讨会论文集(美国纽约州纽约市,2013 年), SOSP ’13,ACM,👉 69-84 页。
- PASZKE, A.、GROSS, S.、CHINTALA, S.、CHANAN, G.、 YANG, E. 、 DEVITO, Z. 、 LIN, Z. 、 DESMAISON, A. 、 ANTIGA, L. 和 LERER, A. PyTorch 中的自动微分。
- QU, H. 、 MASHAYEKHI, O. 、 TEREI, D. 和 LEVIS, P. Canary : 高性能云计算的调度架构。 arXiv 预印本 arXiv:1602.01412 (2016)。
- ROCKLIN, M. Dask:使用阻塞算法和任务调度的 并行计算。 👉 14 届 Python 科学会议论文集(2015 年),K. Huff和 J. Bergstra,编辑,👉 130 – 136 页。
- SALIMANS, T., HO, J., CHEN, X., AND SUTSKEVER, I. 进 化策略作为强化学习的可扩展替代方案。 arXiv 预印本 arXiv:1703.03864 (2017)。
- SANFILIPPO, S. Redis : 一种开源内存数据结构存储。 https://redis.io/, 2009.
- SCHULMAN, J.、WOLSKI, F.、DHARIWAL, P.、RADFORD, A. 和 KLIMOV, O. 近端策略优化算法。 arXiv 预印本 arXiv:1707.06347 (2017)。
- SCHWARZKOPF, M.、KONWINSKI, A.、ABD-EL-MALEK, M. 和 WILKES, J. Omega:适用于大型计算集群的灵活、可扩展的调度程序。👉八届 ACM 欧洲计算机系统会议论文集(美国纽约州纽约市,2013 年),EuroSys '13,ACM,👉 351- 364 页。
- SERGEEV, A. 和 DEL BALSO, M. Horovod:张量流中快速、简 单 的 分 布 式 深 度 学 习 。 arXiv 预 印 本 arXiv:1802.05799 (2018)。
- SILVER, D.、黄, A.、麦迪逊, C. J.、GUEZ, A.、SIFRE, L. 、 VAN DEN DRIESSCHE, G. 、 SCHRITTWIESER, J. 、 ANTONOGLOU, I.、PANNEERSHELVAM, V.、LANCTOT, M. , 等 人。通过深度神经网络和树搜索掌握围棋游戏。自然 529, 7587 (2016), 484–489。
- SILVER, D. 、 LEVER, G. 、 HEESS, N. 、 DEGRIS, T. 、 WIERSTRA, D. 和 RiedMiller, M. 确定性策略梯 度算法。在 ICML(2014)中。
- SUTTON, R. S. 和 BARTO, A. G. 强化学习:简介 。麻省理工学院出版社剑桥,1998 年。
- THAKUR, R.、RABENSEIFNER, R. 和 GROPP, W. MPICH 中集 体通信操作的优化。国际高性能计算应用杂志 19, 1 (2005), 49–66。
- TIAN, Y., GONG, Q., SHANG, W., WU, Y., AND ZITNICK, C. L. ELF:一个广泛的、轻量级的、灵活的实时策略游戏研究平台。神经信息处理系统 (NIPS) 的进展 (2017)。
- TODOROV, E.、EREZ, T. 和 TASSA, Y. Mujoco:用于基于 模型的控制的物理引擎。智能机器人和系统 (IROS),2012年 IEEE/RSJ 国际会议 (2012),IEEE,👉 5026–5033 页。
- VAN DEN BERG, J.、MILLER, S.、DUCKWORTH, D.、HU, H.、 WAN, A.、FU, X.-Y.、GOLDBERG, K. 和 Abbeel, P. 手 术 任务的超人表现由机器人使用人类引导演示中的迭代学习来实现。机器人与自动化 (ICRA),2010 年 IEEE 国际会议 (2010),IEEE,👉 2074–2081 页。
- VAN RENESSE, R., AND SCHNEIDER, F. B. 用于支持高 吞吐 量和可用性的链复制。👉六届操作系统设计与实现研讨会论文集 - 👉 6 卷(美国加利福尼亚州伯克利,2004 年), OSDI’04,USENIX 协会。
- 文卡塔拉曼,S.,潘达,A.,奥斯特豪特,K.,戈 德西,A.,阿姆布鲁斯特, M.,雷希特,B.,富兰克林,M.,和斯托伊卡,
- Drizzle:快速且适应性强的大规模流 处理。👉二十六届 ACM 操作系统原理研讨会论文集(2017 年),SOSP ’17, ACM。
- WHITE, T. Hadoop:权威指南。奥莱利媒体公司,2012 年。
- ZAHARIA, M.、CHOWDHURY, M.、DAS, T.、DAVE, A.、MA, J. 、MCCAULEY, M. 、FRANKLIN, M. J.、SHENKER, S. 和 STOICA, I. 弹性分布式数据集:故障-内存集群计算的容忍抽象。👉 九届 USENIX 网络系统设计和实现会议记录 (2012),USENIX 协会,👉 2-2 页。
- ZAHARIA, M. 、 XIN, R.S. 、 WENDELL, P. 、 DAS, T. 、 ARMBRUST, M. 、 DAVE, A. 、 MENG, X. 、 ROSEN, J. 、 VENKATARAMAN, S. 、 FRANKLIN, M. J. 、 GHODSI, A. 、 GONZALEZ, J. 、 SHENKER, S. 和 STOICA, I. Apache
Spark:用于大数据处理的统一引擎。交流。 ACM 59, 11
(2016 年 10 月),56–65。