在上篇文章中已经提到,Ray 是基于 Actor 模型的,Actor 并不是一种编程固定的编程框架,而是一种编程模型,简化来说可以看成是可以异步发送消息但不共享状态(比如锁)的多线程。
从更高的业务层面去考虑,即使不共享状态仍然有可能死锁,没有银弹,并不是换一种模型就能解决的,可以看下文后链接:为什么 Spark 的底层通信选择 Netty 而不是 Akka (JVM actor 实现)。
Spark 从自身的灵活性考虑,并且只用到了一部分的Akka 功能完全没必要牺牲兼容性来采用Akka;Ray 的目标则不同,它是定位于辅助增强学习(最早)训练出发而设计的,提供了动态的执行结构和低延时。Spark 也有 Streaming,但延时并不理想。
简而言之,Ray 的设计目标是比 Spark 、MapReduce 数据流更灵活,比 Orleans 等 Actor 模型多了 fault tolerance 和 exactly-once,比 Mesos 的两层调度更高效,比 Tensorflow 、MXNet 更易用。
这目标真的有点大……
Ray 最初是为增强学习而设计的(论文 Ray: A Distributed Framework for Emerging AI Applications),下文简要介绍下。
增强学习
增强学习可以看成一个智能体在环境中用给定的策略不断尝试,然后根据评价来改进参数的过程。举个例子,围棋,棋手就是智能体,环境是棋盘和对手,策略则是下哪一步棋,胜多少目就是评价。
增强学习的特点是:数据是动态变化而不是静态的,一个智能体在给定的环境下根据规则可能需要数百万次模拟(例如有剪枝的棋盘探索)才能逐步确定最优的参数;
2. 模型对于环境做出的行动并不都是有效的,执行流需要是动态可变,异构可剪枝的;
3. 模型需要执行的速度需要足够快来进行足够多的模拟,在一些实时任务中,例如自动驾驶、 机器人姿态控制等实时性是非常重要的。
Ray 提供了什么
Ray 实现了动态的计算图模型并在顶层提供了 Actor 编程模型的抽象,可以支持有状态的组件,所以可以很方便的引入到其它框架中。
Ray 的关键思想有两个:
将所有的系统控制状态存储在全局中心(global control store),这样系统的其它部分就可以是无状态的(应该是说需要的时候从中心“查”就可以了),节点可以轻易地水平扩展和重启;为了实现更好的性能,global control store 允许分片(sharding)和复制(replication)。图片来自论文Figure 4
设计了一个自底向上的分布式 scheduler , 分为 local 和 global , local 负责调度任务执行或转发任务,每个节点有一个 local scheduler,由 driver 提交任务,worker 来执行。Object Store 存储了各种值,例如 被 remote 修饰的远程函数,提交的数据, 任务状态等等。
Ray 的设计有几个原因:没有使用批调度(可以降低任务提交开销),因为批调度需要中心节点去协调(分割确认数据?),影响了扩展性;
hierarchical scheduling 需要假定任务图是静态的;
平行调度也需要中心节点;
Ray 设计了新的调度方案,首先任务时提交给节点自己的 local scheduler,除非节点超载(通过判断本地任务队列长度)或者输入在远端,如果它无法调度则发送给 global scheduler;节点与 GCS 保持心跳连接并定时发送负载信息,global scheduler 判断 GCS 中的信息来平衡不同节点间的负载。
图片来自论文Figure 5
Ray 假设了函数(被 @ray.remote 修饰的函数)自身需要满足幂等性(即同样的输入多次执行结果一致并且没有副作用),这简化了重试操作和错误恢复(但 actor 可以是有状态的)
详细的性能测试见原文 Ray: A Distributed Framework for Emerging AI Applications:A Distributed Framework for Emerging AI Applicationsarxiv.org
参考文献:How does Actors work compared to threads?stackoverflow.com为什么 spark 2.0 底层通信不用 Akka 而转用 netty ?www.zhihu.com
https://github.com/akka/akkagithub.comWhy Spark 1.6 does not use Akka?stackoverflow.com
akka/akkagithub.com