ArXiv: 1712.05889
🎯 Pitch
A single unified framework can replace the patchwork of specialized systems for cutting-edge reinforcement learning, scaling beyond 1.8 million tasks per second. Ray achieves this by completely decoupling its control state from scheduling and data transfer, making every component except a sharded key-value store stateless. It cuts training time for evolution strategies to just 3.7 minutes—more than doubling the speed of the best previously published result—and reduces proximal policy optimization cost by 4.5× through heterogeneity-aware scheduling.
1. Executive Summary
This paper proposes Ray, a general-purpose distributed framework that unifies the tightly-coupled training, serving, and simulation workloads required by emerging reinforcement learning applications. Built around a unified interface that expresses both task-parallel and actor-based computations on a single dynamic execution engine, Ray employs a distributed scheduler and a fault-tolerant global control store to achieve scalability beyond 1.8 million tasks per second on 100 nodes while matching or exceeding the performance of specialized systems—outperforming an OpenMPI allreduce implementation by up to 2× on large objects, cutting evolution strategies training time to 3.7 minutes (more than twice as fast as the best published result), and reducing proximal policy optimization cost by 4.5× through heterogeneity-aware scheduling. The architecture's central design principle—storing all control state in a sharded, chain-replicated key-value store while keeping every other system component stateless—enables transparent lineage-based fault tolerance, elastic scaling, and millisecond-level scheduling latency, establishing that a single general-purpose framework can subsume the one-off distributed systems that practitioners previously built for RL applications only when the control plane is fully decoupled from both scheduling and data transfer.
2. Context and Motivation
The Core Problem: A Generation Gap in Distributed Computing Frameworks
The fundamental problem this paper addresses is deceptively simple: no existing distributed system can simultaneously support the three tightly-coupled workloads that form the backbone of modern reinforcement learning applications—training, serving, and simulation. This is not a performance optimization problem; it's an architectural gap. The paper argues that the very structure of existing frameworks, designed for different eras of computing, makes them fundamentally incapable of handling the RL workload profile.
To understand why this gap exists, we must recognize that distributed computing frameworks have evolved in waves, each optimized for a particular class of applications that dominated its era. The paper traces this evolution in its opening paragraphs. The first wave—"Big Data" frameworks like MapReduce, Apache Spark, and Dryad—were designed for batch analytics: long-running jobs that process enormous datasets through coarse-grained, homogeneous operations. The second wave—deep learning frameworks like TensorFlow, MXNet, and PyTorch—targeted supervised learning: static computation graphs of linear algebra operations that train on labeled data using specialized hardware. Both waves produced systems that excel at their intended workloads but are architecturally misaligned with the demands of reinforcement learning.
RL presents a fundamentally different computation pattern. As Figure 1 illustrates, an RL system operates in a continuous loop: the policy is evaluated through interaction with an environment (simulation or physical world), the resulting trajectories are fed back to improve the policy through distributed training, and the updated policy must then be served—often with millisecond latency—to drive subsequent interactions. These three phases are not independent stages that can be handled by separate systems; they are tightly interleaved, with the output of one phase directly determining the input and timing of the next. When a simulation produces a trajectory, that data must flow immediately into the training pipeline, and the updated policy must be available for the next simulation step. The latency between these transitions directly impacts the end-to-end learning speed.
The paper frames this through a concrete example in the introduction: "RL methods often rely on simulation to evaluate policies. Simulations make it possible to explore many different choices of action sequences and to learn about the long-term consequences of those choices." This isn't optional—"current RL algorithms are not sample-efficient enough to rely solely on data obtained from interactions with the physical world" (Section 2). This dependence on simulation drives the scale requirement: meaningful policy improvement requires vast numbers of simulation episodes, each of which may take anywhere from milliseconds (a chess move) to minutes (a physics-based driving scenario).
Why This Problem Matters: The Systems Engineering Tax
The paper identifies a serious consequence of the architectural gap: researchers and practitioners are forced to build one-off distributed systems from scratch for each RL application. Section 2 states this bluntly:
"researchers and practitioners today build one-off systems for specialized RL applications [58, 41, 54, 44, 49, 5]. This approach imposes a massive systems engineering burden on the development of distributed applications by essentially pushing standard systems challenges like scheduling, fault tolerance, and data movement onto each application."
This is not merely inconvenient—it's a fundamental drag on research progress. Each new RL algorithm requires its own distributed infrastructure. When someone develops a novel training approach, they must also solve distributed scheduling, data movement, and failure recovery before they can evaluate it at scale. Worse, optimizations discovered in one system (like the hierarchical aggregation the Ray authors use to accelerate evolution strategies) cannot be easily ported to another, because they're baked into application-specific communication protocols rather than expressed in a shared framework.
The economic implications extend beyond academic research. The paper notes that "several companies are running [Ray] in production" (Section 7), suggesting this systems engineering tax affects industrial deployments as well. For organizations investing in RL-based applications—dialogue systems, autonomous vehicles, robotic manipulation—the cost of building and maintaining custom distributed infrastructure represents a substantial fraction of the total development effort. A general-purpose framework that eliminates this tax would redirect engineering resources from infrastructure to algorithm development.
Where Existing Frameworks Fall Short
The paper provides a systematic critique of existing systems, organized by their intended use cases. Each class of system fails for different but specific reasons.
Batch Processing and Dataflow Systems
MapReduce, Spark, and Dryad implement variations of the Bulk Synchronous Parallel (BSP) execution model. In BSP, computation proceeds in synchronized stages: all tasks in a stage must complete before any task in the next stage begins. This works well for data-parallel operations on homogeneous data (map, reduce, filter), but the paper argues it's "too restrictive for a fine-grained and dynamic simulation workload" (Section 6).
The BSP model's assumptions break down for RL in multiple ways. First, simulation tasks are heterogeneous in duration: one simulation episode might take 10 milliseconds while another takes 10 minutes. In BSP, the entire stage waits for the slowest task—a barrier synchronization that the paper demonstrates causes significant throughput degradation (Table 4 shows Ray achieving 4.03M timesteps/second vs. 2.16M for MPI/BSP on 256 cores). Second, BSP assumes tasks within a stage are functionally identical, which precludes the nested, dynamically-created computation patterns that RL algorithms naturally produce—where a simulation result determines whether further simulations are needed.
The paper further notes that Spark and MapReduce "lack support for dynamic task graphs" (Section 6). An RL training loop creates tasks dynamically: policy evaluation spawns variable numbers of rollout tasks, and the results of those rollouts determine how many more rollouts to generate before the next policy update. A framework that requires the full task graph to be specified before execution cannot express this pattern naturally.
Task-Parallel Systems (CIEL, Dask)
CIEL and Dask do support dynamic task graphs with nested tasks—an important capability for RL. However, the paper identifies two critical shortcomings. The first is the absence of an actor abstraction. CIEL and Dask provide only stateless tasks. For RL, this is problematic because training involves stateful computation: a parameter server maintains model weights across many updates, and GPU-based training processes need state collocated with their computation. Without actors, developers must simulate stateful computation through external storage or manual state management, which introduces serialization overhead and breaks the programming model.
The second shortcoming is architectural: both systems rely on a centralized scheduler/master that stores all metadata. The paper quantifies the consequences directly (Section 6):
"Dask reports a maximum scheduler throughput of 3k tasks/s on 512 cores. With a centralized scheduler, each round of allreduce would then incur a minimum of ~5ms of scheduling delay, translating to up to 2× worse completion time."
For an allreduce operation at the scale Ray targets (16 nodes, 100MB objects, 32 rounds of 16 tasks completing in 200ms), a centralized scheduler's 5ms overhead per round becomes prohibitive. The paper's Figure 12b empirically demonstrates this: injecting just 1-10ms of additional scheduling latency causes the allreduce completion time to degrade by nearly 2×. The centralized scheduler design, which was perfectly adequate for the coarse-grained batch workloads of the Big Data era, becomes the bottleneck at the fine granularity and low latency that RL requires.
Deep Learning Frameworks (TensorFlow, MXNet)
TensorFlow and MXNet are exceptionally good at what they target: executing static directed acyclic graphs (DAGs) of linear algebra operations efficiently on heterogeneous hardware. But RL's simulation and serving phases don't look like static DAGs. The paper acknowledges that "TensorFlow Fold provides some support for dynamic task graphs, as well as MXNet through its internal C++ APIs, but neither fully supports the ability to modify the DAG during execution in response to task progress, task completion times, or faults" (Section 6).
This limitation is not an oversight—it's a consequence of design priorities. Deep learning frameworks optimize for throughput on large, regular computation graphs by using ahead-of-time compilation, graph partitioning, and memory planning. Dynamic task graph support (where the graph changes based on intermediate results) conflicts with many of these optimizations. The paper's position is not that these frameworks should add such support, but rather that a separate system layer is needed to orchestrate the higher-level control flow while delegating the low-level tensor operations to TensorFlow/MXNet.
The paper explicitly states this boundary: "such a framework is not intended for implementing deep neural networks or complex simulators from scratch. Instead, it should enable seamless integration with existing simulators and deep learning frameworks" (Section 2). Ray positions itself as the orchestration layer, not a replacement for the compute engines.
Actor Systems (Orleans, Akka)
Orleans and Akka provide the stateful computation model that RL needs, and they're designed for building highly available distributed systems. However, the paper identifies a fundamental mismatch in their fault tolerance model.
In Orleans and Akka, fault tolerance for stateful actors requires explicit developer intervention: "the Orleans developer must explicitly checkpoint actor state and intermediate responses" (Section 6). For stateless computation (which should be the common case for simulation tasks), these systems provide either at-least-once semantics (Orleans) or at-most-once semantics (Akka), but neither provides exactly-once semantics with transparent recovery—the system the paper argues is essential for RL workloads.
Ray's fault tolerance model is fundamentally different. By logging every task and method invocation in the global control store and making all arguments and results immutable, Ray achieves exactly-once semantics through lineage-based replay. If a node fails, Ray transparently re-executes the lost computation from its recorded lineage, without the developer writing any checkpoint logic. The paper demonstrates this empirically: Figure 11a shows that when nodes are forcibly removed, Ray's task throughput dips briefly but recovers to original levels as it reconstructs lost dependencies. Figure 11b shows actor recovery: when 2 of 10 nodes are killed (taking 400 of 2000 actors with them), the system automatically reconstructs the lost actors from their last checkpoint and resumes within ~70 seconds.
For RL applications, this matters enormously. The paper's Section 7 describes a concrete economic benefit: "fault tolerance helps save money since it allows us to run on cheap resources like spot instances on AWS." Spot instances can be 4× cheaper than on-demand instances, but they can be terminated with minimal notice. A system that transparently handles such terminations—by recomputing lost work—makes this cost optimization viable. The paper quantifies this: for the PPO application, combining fault tolerance with resource-aware scheduling together "cut costs by 18×" compared to a fault-intolerant MPI implementation running on on-demand instances (Section 5.3).
Specialized RL Systems
The paper acknowledges that researchers have built highly optimized one-off systems for specific RL algorithms: the OpenAI baselines for PPO, the reference implementation for evolution strategies, distributed prioritized experience replay, and the custom infrastructure behind AlphaGo. These systems achieve good performance for their specific algorithms. The problem is portability and extensibility: optimizations in one system don't transfer to another, and adding new capabilities (like hierarchical aggregation for ES, or heterogeneous resource scheduling for PPO) requires deep changes to application-specific communication protocols rather than configuration-level adjustments in a shared framework.
Section 5.3 makes this concrete. The reference ES implementation "had several hundred lines of code dedicated to a protocol for communicating tasks and data between workers, and would require further engineering to support optimizations like hierarchical aggregation." In Ray, the same optimization—which cuts training time by more than 2×—is implemented using nested actors, a natural expression of the programming model. Similarly, the PPO reference implementation had two separate codebases: one for MPI-based distributed execution and one optimized for single-node GPU execution. Ray's heterogeneity-aware scheduling allows a single implementation to run efficiently in both settings, automatically placing GPU workloads on GPU-equipped nodes and CPU-only workloads on cheaper instances.
How Ray Positions Itself
Ray's position is carefully scoped. The paper is explicit about what Ray is not:
"Ray does not aim to substitute for serving systems like Clipper and TensorFlow Serving, as these systems address a broader set of challenges in deploying models, including model management, testing, and model composition. Similarly, despite its flexibility, Ray is not a substitute for generic data-parallel frameworks, such as Spark, as it currently lacks the rich functionality and APIs (e.g., straggler mitigation, query optimization) that these frameworks provide." (Section 1)
Rather than replacing specialized systems, Ray positions itself as the missing orchestration layer that connects them. An RL application built on Ray might use TensorFlow for gradient computation, an existing simulator like OpenAI Gym or MuJoCo for environment simulation, and Ray's task and actor abstractions to manage the dynamic flow of data and control between these components. The key architectural claim is that this orchestration requires a fundamentally different system design than any existing framework provides—one with a unified programming model for both stateless and stateful computation, a fully distributed control plane that keeps the scheduler off the critical path, and transparent lineage-based fault tolerance that works uniformly across tasks and actors.
The paper's claim to novelty rests on this unification. While individual elements of the design have precedents—dynamic task graphs in CIEL, actors in Orleans, distributed scheduling in Sparrow, lineage-based fault tolerance in Spark—no prior system combines them with the performance characteristics required for RL. More importantly, no prior system demonstrates that the decoupling of control state from scheduling is the key architectural principle that makes this unification possible at the required scale. This principle—storing all control state in a sharded, chain-replicated key-value store while keeping every other component stateless—is what enables Ray to achieve millisecond scheduling latency, linear scalability to millions of tasks per second, and transparent fault tolerance simultaneously, all from a single general-purpose framework.
3. Technical Approach
3.1 Reader Orientation
Ray is a distributed cluster-computing framework that acts as a general-purpose orchestration layer for reinforcement learning applications, sitting between the developer's Python code and the specialized compute engines (TensorFlow, PyTorch, OpenMPI, simulators) that do the heavy numerical lifting. The paper's central technical insight is that by unifying two previously separate programming abstractions—stateless tasks and stateful actors—on top of a single dynamic execution engine whose control plane is fully decoupled from both scheduling and data transfer, you can simultaneously achieve millisecond scheduling latency, transparent fault tolerance through lineage-based replay, and linear scalability to over 1.8 million tasks per second, all while expressing the heterogeneous, fine-grained, dynamically-evolving computation graphs that reinforcement learning naturally produces.
3.2 Big-Picture Architecture (Diagram in Words)
Ray's architecture consists of two layers and seven major component types, connected through a shared-state design principle:
-
Application Layer: Three process types that the user interacts with or that execute application code:
- Driver: The process executing the user's program (e.g., a Jupyter notebook or Python script that calls
train_policy.remote()). - Worker: A stateless process that executes remote function invocations (tasks). Workers are started automatically by the system and assigned work by schedulers.
- Actor: A stateful process explicitly instantiated by a driver or worker that exposes methods invoked remotely, executing them serially while maintaining mutable internal state across invocations.
- Driver: The process executing the user's program (e.g., a Jupyter notebook or Python script that calls
-
System Layer: Three horizontally-scalable infrastructure components that manage scheduling, data, and metadata:
- Global Control Store (GCS): A sharded, chain-replicated key-value store with pub-sub functionality that maintains all control state—the object table (object ID → locations), the task table (task ID → status), the function table (function name → definition), and event logs (lineage records for every task and actor method invocation). The GCS is the single source of truth for system state.
- Distributed Scheduler: A two-level hierarchy of global schedulers (which balance load across nodes and make placement decisions considering resource constraints and data locality) and per-node local schedulers (which attempt to execute tasks locally first, forwarding to the global scheduler only when the node is overloaded or lacks required resources).
- In-Memory Distributed Object Store: Per-node shared-memory storage (implemented via Apache Arrow) for immutable task inputs and outputs. Objects are replicated between nodes on demand, kept entirely in memory, and evicted via LRU to disk when memory pressure occurs.
The critical architectural principle is that every system-layer component except the GCS is stateless. The distributed scheduler and object store do not maintain their own persistent metadata; they read and write it from the GCS. This decoupling is what makes the scheduler and object store independently and horizontally scalable—if the global scheduler becomes a bottleneck, you add more replicas, all reading from the same GCS. If the GCS becomes a bottleneck, you add more shards.
3.3 Roadmap for the Deep Dive
I will explain Ray's design in the following order, building from the programmer's view down to the distributed mechanisms:
- First, the programming and computation model (Section 3 of the paper)—what the developer writes and the task graph that gets constructed—because the system's architecture is shaped entirely by the requirements this model imposes.
- Second, the Global Control Store (GCS)—what data it holds, how it achieves fault tolerance through chain replication with per-shard configuration, and why decoupling it from the scheduler is the lynchpin of the entire architecture.
- Third, the bottom-up distributed scheduler—the two-level hierarchy, the scheduling algorithm, the load-aware heuristic, and how it achieves both data locality and horizontal scalability without a centralized bottleneck.
- Fourth, the in-memory distributed object store—how shared memory, Apache Arrow, and on-demand replication minimize task latency, how the LRU eviction policy manages memory, and how objects are striped across TCP connections for transfer.
- Fifth, fault tolerance—how lineage-based reconstruction works uniformly for both tasks and actors, how checkpointing bounds actor recovery time, and the exactly-once semantics guarantee.
- Sixth, the end-to-end walkthrough (Figure 7)—tracing a single remote function call from submission through scheduling, argument resolution, execution, and result retrieval to make the component interactions concrete.
3.4 Detailed, Sentence-Based Technical Breakdown
This is primarily a distributed systems design paper whose core idea is that RL workloads require a dynamic task graph computation model that unifies task-parallel and actor-based abstractions, and that the only way to achieve the necessary scalability and latency for this model is by decoupling all control state into a sharded, fault-tolerant key-value store while keeping every other system component stateless.
The Programming Model: Tasks, Actors, and Futures
Ray's programming model is built around three core abstractions—remote functions (tasks), remote classes (actors), and futures—together with three API primitives that expose concurrency and data dependencies. This is not just an interface; it's the specification of the computation model that the entire system layer is designed to execute efficiently.
Tasks (Remote Functions). A task represents the execution of a Python function on a stateless worker process somewhere in the cluster. The developer marks a function with @ray.remote to declare that it can be invoked remotely. When the function is called using f.remote(args), execution is asynchronous and non-blocking: the call returns immediately with one or more futures (object references) that represent the eventual result, while the actual computation is dispatched to the system layer for scheduling.
The critical semantic contract for tasks is that they operate on immutable objects and are expected to be stateless and side-effect free—their outputs must be determined solely by their inputs. This is not enforced by the runtime (Ray cannot inspect Python functions to verify purity), but it is the assumption that the system's fault tolerance model depends on. Because a task is stateless and deterministic given its inputs, re-executing it with the same inputs always produces the same output, which is what enables transparent recovery through lineage-based replay on failure.
The paper's API table (Table 1) defines three operations on tasks:
futures = f.remote(args): Invoke remote functionfwith arguments (which can be concrete objects or futures) and return one or more futures. Non-blocking.objects = ray.get(futures): Block until the values associated with one or more futures are available, then return them.ready_futures = ray.wait(futures, k, timeout): Return the subset of futures whose corresponding tasks have completed, returning as soon as eitherkhave completed or the timeout expires. Non-blocking.
The ray.wait primitive is essential for handling heterogeneous task durations—a core RL requirement (Section 2). In a simulation workload where some rollouts complete in milliseconds and others in minutes, ray.wait allows the policy improvement loop to process results as they arrive rather than waiting for the slowest simulation in every batch. This is what enables dynamic, asynchronous computation patterns rather than BSP-style barriers.
Actors (Remote Classes). An actor represents a stateful computation—an instance of a class that lives on a particular worker process and maintains mutable internal state across method invocations. The developer marks a class with @ray.remote and instantiates it using Class.remote(args), which creates the actor on some node and returns a handle. Methods on the actor are invoked using actor.method.remote(args), which returns a future and is non-blocking, identical in interface to a remote function call.
The critical semantic difference from tasks is that actor methods are executed serially on the actor's owning process—if two callers invoke methods on the same actor simultaneously, Ray serializes those invocations, executing them one at a time in invocation order. This serialization is the mechanism that preserves state consistency: because each method call sees the state left by the previous call, the programmer can reason about mutable state without locks or explicit synchronization.
The paper identifies three primary use cases for actors (Section 3.2, Table 2 discussion):
-
Parameter servers: A sharded set of actors, each holding a partition of model weights in memory, that receive gradient updates from training workers and apply them in-place. Because updates are performed directly on internal state (no serialization/deserialization per update), actors provide "much more efficient fine-grained updates" than tasks, which would require each update to read the entire parameter vector, deserialize it, apply the gradient, serialize the result, and write it back.
-
GPU-based iterative computation: An actor pinned to a GPU can keep model weights in GPU memory across training iterations, amortizing the cost of data transfer to/from the GPU across many gradient steps.
-
Third-party simulator wrappers: Many simulators (e.g., MuJoCo, custom game engines) maintain opaque internal state that cannot be serialized. An actor can wrap such a simulator, exposing a
rollout(policy)method that runs the simulation and returns the trajectory, while the simulator's internal state remains resident in the actor's process.
The paper also notes a crucial limitation for actors: poor data locality support (Table 2). Once an actor is placed on a node, it cannot move. If a subsequent task needs to send a large object (e.g., a video frame) as input to an actor method, the data must be transferred to the actor's node, incurring network cost. Tasks, by contrast, can be scheduled on the node that already holds their input data, avoiding transfer entirely. This is the fundamental tradeoff between tasks and actors that the design Table 2 enumerates.
Nested remote functions. A key design choice is that remote functions can invoke other remote functions. This is "critical for achieving high scalability (Section 4), as it enables multiple processes to invoke remote functions in a distributed fashion." Without nesting, only the driver could create tasks, making the driver a sequential bottleneck. With nesting, a worker executing one task can spawn additional parallel tasks, enabling the computation to fan out dynamically—a pattern essential for hierarchical aggregation (Section 5.3.1) and for expressing parallel simulation workloads where the number of rollouts depends on intermediate results.
Resource specification. The API allows developers to specify resource requirements for both tasks and actors, e.g., @ray.remote(num_gpus=2). This enables the scheduler to make heterogeneity-aware placement decisions: placing GPU-requiring tasks on GPU-equipped nodes, and scheduling CPU-only tasks on cheaper instances. The economic implications are quantified in Section 5.3.2: for PPO, using CPU-only tasks on cheaper high-CPU instances alongside GPU tasks reduced cost by 4.5× compared to the homogeneous MPI deployment where all processes required identical resources.
The Computation Model: Dynamic Task Graphs
Underneath the programming model, Ray represents every application as a dynamic task graph—a directed graph that evolves during execution as tasks and actor methods are invoked and complete. This model is the bridge between what the developer writes and what the system executes.
Graph structure. The task graph has two types of nodes and three types of edges (Section 3.2, Figure 4):
-
Data object nodes: Immutable values produced by tasks or actor methods. These are the intermediate results that flow through the computation.
-
Task/method nodes: Remote function invocations (tasks) or actor method invocations. These represent units of work to be executed.
-
Data edges: Connect a task node to a data object node. A data edge from task
$T$to object$D$means$D$is an output of$T$. A data edge from object$D$to task$T'$means$D$is an input to$T'$. Data edges capture producer-consumer dependencies:$T'$cannot execute until$D$has been produced by$T$. -
Control edges: Connect a task
$T_1$to a task$T_2$. A control edge exists when$T_1$(via nested remote invocation) calls$T_2$. Control edges ensure that the calling task's process can track the status of the tasks it spawns, but they do not imply data dependencies—$T_2$'s inputs might come from entirely different parts of the graph. -
Stateful edges: Connect successive method invocations on the same actor. If method
$M_j$is invoked after method$M_i$on actor$A$, a stateful edge exists from$M_i$to$M_j$. This edge captures the implicit data dependency created by mutable actor state—$M_j$must execute after$M_i$completes, because$M_j$reads the state that$M_i$wrote.
Stateful edges are the mechanism that embeds actors in an otherwise stateless task graph. Without them, the task graph would have no way to express ordering constraints across actor method invocations—two callers could invoke methods on the same actor simultaneously with no ordering guarantee, leading to race conditions on the actor's state. By chaining all methods on an actor with stateful edges, Ray enforces sequential execution without requiring the programmer to explicitly manage synchronization or locking.
Dynamic construction. Unlike static computation graphs (e.g., TensorFlow's original graph mode), the task graph is built incrementally at runtime. When a remote function or actor method is invoked, a new node is added to the graph. When a task completes, its output objects become available, which may trigger additional tasks whose data dependencies are now satisfied. This dynamism is essential for RL, where "the results of a computation can determine future computations" (Section 2)—for example, a simulation result might indicate that further exploration is needed, causing the driver to spawn additional simulation tasks dynamically.
Lineage tracking. Because the task graph records every data edge, control edge, and stateful edge, it serves as a complete lineage record for every data object in the system. If a data object $D$ is lost (because the node storing it fails), Ray can reconstruct $D$ by re-executing the task or actor method that produced it, using the recorded edges to determine which inputs to feed to the re-execution. This lineage-based recovery is the foundation of Ray's fault tolerance (Section 4.2.3) and distinguishes it from actor frameworks like Akka and Orleans, which require explicit developer checkpointing.
The add(a, b) example in detail. The paper's Figure 4 shows a concrete task graph for the train_policy() function in the pseudocode (Figure 3). The graph captures the distributed training loop: train_policy (T0) invokes create_policy (T1), whose output policy1 feeds into the first call to update_policy (T2). Meanwhile, the ten simulator actors each execute rollout methods (A11, A12, ...), which read policy1 through data edges and produce rollout11, rollout12, etc. These rollout objects feed into update_policy (T2), which produces policy2. The cycle repeats for the next training iteration (A21, A22, T3, ...). The control edges from T0 to all subtasks capture the parent-child relationship, and the stateful edges within each actor (A11→A21, A12→A22) capture the serial invocation order.
The Global Control Store (GCS): System-Wide State as a Sharded Key-Value Store
The Global Control Store is the most architecturally distinctive component in Ray. The paper describes it as "a unique feature of our design" (Section 4.2.1), and its role is to serve as the single source of truth for all control state in the system, while keeping every other component—schedulers, object stores, workers—completely stateless with respect to system metadata.
What the GCS stores. The GCS maintains several logical tables, each tracking a different category of metadata (Section 4.2.1, Figure 5):
-
Object Table: Maps each object ID (the unique identifier generated when a task or actor method is invoked) to the set of node locations where that object is stored. When the distributed object store on node N1 needs to fetch object
idato satisfy a task's input dependency, it queries the Object Table to discover thatidais present on nodes N1 and N3. This table is also what enables the global scheduler to make locality-aware placement decisions: when deciding where to run a task, the scheduler looks up its input objects' locations and prefers nodes that already hold them. -
Task Table: Records the status of every task and actor method invocation—queued, scheduled, running, completed, or failed. This is the system's record of what work has been submitted and whether it has been executed.
-
Function Table: Maps function names (e.g.,
"add") to their serialized definitions. When a remote function is declared with@ray.remote, its definition is published to the Function Table. Workers retrieve function definitions from this table when they need to execute a function they haven't seen before, enabling workers to be started without pre-installing all application code. -
Event Logs: Stores lineage records—essentially a log of which task invoked which other task (control edges), which objects were produced by which tasks (data edges), and which actor method invocations occurred in which order (stateful edges). These logs are the durable record that enables lineage-based fault tolerance.
Why decoupling the GCS from the scheduler matters. The paper argues that coupling metadata storage with scheduling—the natural design in many dataflow systems—creates two scaling bottlenecks that are fatal for RL workloads (Section 4.2.1).
The first bottleneck is lineage storage scalability. Existing lineage-based systems like Spark store lineage on the driver/master node. For coarse-grained batch workloads with hundreds of tasks, this is fine—the driver has ample memory and compute to track that much lineage. But Ray targets millions of fine-grained tasks per second. In a workload like the ES application (Section 5.3.1), each policy iteration spawns roughly 10,000 simulation tasks, and training may run hundreds of iterations. Centralizing lineage on a single node would exhaust its memory and turn it into a bottleneck. By decoupling lineage storage into a sharded GCS running on multiple nodes, Ray allows lineage capacity to scale independently of the scheduler.
The second bottleneck is scheduler throughput for data-intensive operations. In systems where the centralized scheduler also stores object location metadata, every lookup of "where is object X?" must go through the scheduler. For an operation like allreduce, which involves many rounds of tasks each reading and writing objects, this adds a scheduler round-trip to every data transfer. The paper quantifies the impact in Section 6: with a centralized scheduler achieving 3,000 tasks/second, each round of allreduce incurs ~5ms of scheduling delay. Across 32 rounds, this adds up to 160ms—nearly doubling the 200ms completion time Ray achieves by keeping the scheduler off the data transfer path entirely.
By putting object metadata in the GCS (which can be queried directly by any local scheduler or object store), Ray fully decouples task dispatch from task scheduling. When a worker needs to fetch a remote object, it queries the GCS directly to find the object's locations, then initiates the transfer—no scheduler involvement required. This is what enables the allreduce performance shown in Figure 12a.
Implementation details and scaling mechanisms. The GCS is implemented using Redis as the underlying key-value store, with two key mechanisms for scalability and fault tolerance (Section 4.2.4):
-
Sharding: GCS tables are sharded by object and task IDs to distribute load across multiple Redis instances. When the GCS becomes a bottleneck, the system administrator can add more shards, increasing the total throughput capacity. The paper reports (Section 7): "we were able to scale by adding more shards whenever the GCS became a bottleneck."
-
Per-shard chain replication: Each GCS shard is protected by chain replication—a fault-tolerance protocol where writes flow sequentially through a chain of replicas (head → middle(s) → tail), and reads are served by the tail. If any replica fails, a new one can be added to the chain, and the chain is reconfigured transparently. The paper's fault-tolerance experiment (Figure 10a) demonstrates that chain reconfiguration causes a maximum client-observed delay of under 30ms, which includes both failure detection and recovery. The chain starts with 2 replicas; when a chain member is killed (at t ≈ 4.2s), a new member joins, initiates state transfer to catch up on missed writes, and restores the chain to 2-way replication—all while the client continues submitting tasks.
GCS flushing. The paper identifies a practical concern: lineage records accumulate indefinitely as tasks execute, and if left unchecked, the GCS memory footprint grows without bound. Ray implements periodic flushing of task lineage to disk (Figure 10b). Without flushing, a workload of 50 million sequential no-op tasks causes the GCS to exhaust available memory and stall. With aggressive flushing, memory consumption is capped at a user-configurable level. The flush mechanism also serves double duty as a natural snapshot mechanism for long-running applications.
Why centralizing control state is a feature, not a bug. The paper makes an explicit design argument in Section 7: "centralizing control state will be a key design component of future distributed systems." This might seem counterintuitive—isn't centralization the enemy of scalability? The distinction is between centralizing processing (which creates a bottleneck) and centralizing state (which can be sharded and replicated to scale while providing a single consistent view). The GCS centralizes the system's truth (the authoritative record of what objects exist where, what tasks are running, what lineage has been recorded) while distributing the processing (multiple scheduler replicas, multiple object store instances, all reading from and writing to the same logical store). This separation of concerns—truth in one place, execution everywhere else—is what the paper identifies as the key to Ray's scalability.
The Bottom-Up Distributed Scheduler
The scheduler is responsible for deciding where and when each task and actor method executes. Ray's scheduler is designed for the extreme end of the task granularity spectrum: millions of tasks per second, each potentially taking only a few milliseconds. This requirement eliminates centralized scheduler designs, which add tens of milliseconds of scheduling latency per task. The alternative—fully decentralized work-stealing (as in Cilk)—achieves high throughput but struggles with data locality and resource heterogeneity in a distributed setting. Ray's solution is a two-level hierarchical, bottom-up scheduler that combines local scheduling at each node (fast, locality-aware) with global scheduling across nodes (load-balancing, resource-aware).
Architecture and flow. The scheduler consists of (Section 4.2.2, Figure 6):
-
One local scheduler per node: A single-threaded, event-driven process that manages the task queue for its node. It maintains cached state for local object metadata (which objects are present in the local object store?), tasks that are waiting for their inputs to become available locally, and tasks that are ready for dispatch to a worker on that node.
-
One or more global schedulers: Instances that make cross-node placement decisions. They receive tasks that local schedulers have chosen not to handle locally, and they select the best node for each task based on load, resource availability, and data locality.
The bottom-up naming reflects the flow of task submission: tasks created at a node (by a driver, worker, or actor) are submitted first to the node's local scheduler (step 1 in Figure 7a). The local scheduler attempts to execute the task on its own node. It only forwards the task upward to a global scheduler if (a) the local node is overloaded—its local task queue exceeds a predefined threshold—or (b) the local node cannot satisfy the task's resource requirements (e.g., the task requires a GPU and the node has none). This means that in a well-configured cluster where most tasks' resource needs are met locally, the vast majority of scheduling decisions happen at the local level, never touching the global scheduler.
When the global scheduler does receive a task, it makes a placement decision based on three signals, all obtained from the GCS or from heartbeats:
-
Resource availability: Which nodes have sufficient resources (CPU cores, GPUs, custom resources) of the types the task requested? Nodes that lack required resources are eliminated from consideration.
-
Estimated waiting time at each candidate node: This is computed as the sum of: (i) the estimated queueing delay, defined as
task queue size × average task execution time, and (ii) the estimated transfer time for the task's remote inputs, defined astotal size of remote inputs ÷ average transfer bandwidth. The global scheduler obtains queue sizes and resource availability from periodic heartbeats that each local scheduler sends, input locations and sizes from the GCS's Object Table, and maintains exponentially-weighted moving averages of task execution time and transfer bandwidth. -
Data locality: Among nodes with similar estimated waiting times, the scheduler prefers nodes that already hold the task's input objects, since this avoids network transfer before execution.
This is a greedy, heuristic-based scheduling policy—it makes locally optimal decisions per task without global knowledge of the full computation graph. The paper acknowledges this limitation (Section 7): "we must make scheduling decisions without full knowledge of the computation graph." For RL workloads, where the task graph is dynamic and future tasks depend on results of current tasks, full-graph optimization is impossible anyway. The heuristic trades optimality for speed, and the microbenchmarks suggest the tradeoff is favorable.
Scalability and fault tolerance of the scheduler. The architecture scales horizontally in two dimensions:
-
More nodes add more local schedulers—since each node has its own local scheduler, adding nodes increases the total scheduling capacity proportionally. The bottom-up design ensures that most scheduling work stays local, so total scheduling throughput scales roughly linearly with node count, as demonstrated in Figure 8b.
-
More replicas of the global scheduler can be instantiated if it becomes a bottleneck. Because all global scheduler state is stored in the GCS (not in-memory at the scheduler), multiple global scheduler instances can operate concurrently, each reading the same load and object-location information from the GCS and making independent placement decisions. This removes the single-point-of-bottleneck that centralized schedulers suffer from.
The scheduler processes themselves are stateless from a fault-tolerance perspective. If a local scheduler crashes, its node's workers become unreachable and the GCS marks tasks assigned to that node as lost, triggering lineage-based reconstruction elsewhere. If a global scheduler crashes, tasks it was processing can be resubmitted (they're recorded in the GCS's Task Table) and picked up by another global scheduler instance. The GCS heartbeat mechanism detects scheduler failures.
Comparison with related scheduling architectures. The paper positions its bottom-up scheduler against several alternatives (Section 4.2.2, Section 6):
-
Centralized (Spark, CIEL, Dryad): Provides good locality (since the central scheduler has global knowledge) but at latencies in the tens of milliseconds, which is "prohibitive for primitives important to distributed training like allreduce" (Section 4.2.1). The paper's Figure 12b empirically shows that injecting even 1-10ms of scheduling latency degrades allreduce performance by nearly 2×.
-
Work stealing (Cilk): Achieves provably efficient load balancing for dynamic task graphs but "with no central coordinator like Ray's global scheduler, this fully parallel design is also difficult to extend to support data locality and resource heterogeneity in a distributed setting" (Section 6). Pure work stealing assumes homogeneous nodes and uniform task costs, which breaks down when tasks have GPU requirements or when data must be transferred between nodes.
-
Sparrow: A decentralized scheduler where schedulers make independent decisions, but "all tasks of a job are handled by the same global scheduler" (Section 6), limiting load balancing flexibility, and data locality is not considered.
-
Mesos: A two-level hierarchical scheduler, but the top level schedules frameworks (Spark, Hadoop, etc.), not individual tasks. This is too coarse for Ray's per-task scheduling granularity.
-
Canary: Achieves high performance by partitioning the task graph among scheduler instances, but "does not handle dynamic computation graphs" (Section 6).
Ray's key innovation is the combination of bottom-up submission (local-first scheduling that exploits locality and avoids flooding the global scheduler), global load-aware balancing (for tasks that local nodes can't or shouldn't handle), and GCS-mediated state sharing (allowing all scheduler instances—local and global—to access the same object location and load information without putting the GCS on the critical path for every scheduling decision).
The In-Memory Distributed Object Store
The object store is the data plane of Ray: it holds the immutable inputs and outputs of every task and actor method, and it manages the transfer of objects between nodes when tasks require remote data. Its design optimizes for minimum task latency by ensuring that when a task executes, all its inputs are in local shared memory, accessible via zero-copy reads.
Per-node architecture. On each node, the object store is implemented as a shared-memory region (Section 4.2.3). When a task produces an output, it writes the output into this shared-memory store. When a subsequent task on the same node needs that output as an input, it reads directly from the shared-memory region without any serialization, deserialization, or network transfer—the data is already in the local process's address space. This shared-memory design is what enables the low per-task overhead that makes fine-grained parallelism viable.
The data format used is Apache Arrow, a columnar in-memory format designed for zero-copy data sharing between processes. Arrow provides standardized representations for common data types (arrays, tables, tensors) that multiple processes can access without parsing or conversion. By adopting Arrow, Ray avoids the serialization/deserialization cost that would otherwise dominate the latency of small tasks.
Replication and data fetch. When a task is scheduled on a node that does not have all of the task's input objects locally, the local object store is responsible for fetching those objects before execution begins. The process (illustrated in steps 5-7 of Figure 7a) works as follows:
-
The local scheduler checks whether the required objects are present in the local object store. If so, it dispatches the task immediately to a worker.
-
If an object is missing, the local scheduler (or the object store itself) queries the GCS's Object Table to find which nodes hold a copy of the object.
-
The missing object is replicated from one of those nodes to the local object store. The paper notes that objects are transferred using multiple TCP connections per object (striping) to maximize throughput on high-bandwidth links (Section 4.2.4): "To transfer large objects between different object stores, we stripe the object across multiple TCP connections."
-
Once all inputs are local, the task is dispatched to a worker, which accesses them via shared memory.
This design means that each object exists on at least one node (the node where it was originally produced) and is cached on demand on nodes that need it. The replication is pull-based and lazy—objects aren't proactively pushed to all nodes, only fetched when a task on that node requires them. This avoids wasting memory on objects that few nodes need.
Memory management. All objects are kept in memory by default, with an LRU (Least Recently Used) eviction policy to disk when memory pressure occurs. For RL workloads, the paper argues that keeping objects entirely in memory is critical for latency: "this increases throughput for computation-bound workloads, a profile shared by many AI applications" (Section 4.2.3). The LRU policy ensures that frequently accessed objects (like model parameters or recently generated simulation data) stay in memory, while cold objects are evicted to make room.
A key design choice is that the object store is limited to immutable, non-distributed objects—each object must fit on a single node. Distributed objects (e.g., large matrices partitioned across nodes) are handled at the application level by splitting them into collections of futures, each of which is a single-node object. This simplifies the object store's consistency model considerably: since objects are never updated, there are no write-write conflicts, no cache invalidation, and no distributed consistency protocol needed beyond "does this node have the object or not?"
Object store performance (Figure 9). The microbenchmark evaluates two metrics that matter for different workload profiles:
-
Write throughput for large objects: Exceeds 15 GB/s from a single client as object size increases. For large objects (tensors, video frames), the bottleneck is
memcpy—copying the data into the shared-memory region. The object store uses multiple threads (up to 8) to parallelize the copy for objects larger than 0.5MB, and 1 thread for small objects to avoid thread-creation overhead. -
IOPS for small objects: Reaches 18,000 IOPS (input/output operations per second) for small objects. For small objects, the main overheads are serialization/deserialization and inter-process communication between the client and the object store process. This IOPs ceiling determines the maximum rate of fine-grained tasks the system can sustain—if each task reads and writes one small object, the system is limited to roughly 9,000 tasks per second per node (since each task involves at least one write and potentially multiple reads). This explains why achieving 1.8 million tasks per second requires 100 nodes: the per-node IOPs capacity must be multiplied across the cluster.
Task locality benefits (Figure 8a). The paper demonstrates the concrete benefit of Ray's locality-aware scheduling through the task model. When tasks can be placed on the node that holds their inputs (locality-aware), task latency remains essentially constant regardless of input size—the data is available via shared memory, so the transfer cost is zero. When tasks are placed without locality awareness (as is the case for actor methods, which must execute on the actor's node), latency increases by 1-2 orders of magnitude for input sizes of 10-100 MB, because the data must be transferred over the network before execution begins. This quantifies the tradeoff in Table 2: tasks provide "support for object locality" and "fine-grained load balancing," while actors trade away those benefits for "low overhead for small updates" and the ability to maintain mutable state.
Fault Tolerance: Lineage-Based Reconstruction
Ray provides fault tolerance for both tasks and actors through a unified mechanism: lineage-based reconstruction. The core idea is that because all task inputs and outputs are immutable, and because the GCS records the complete lineage (which task produced which object using which inputs), any lost object can be reconstructed by re-executing the task that originally produced it, starting from objects that are still available.
How lineage reconstruction works (Figure 11a). When a node fails—whether due to hardware failure, preemption (spot instance termination), or explicit removal—the following sequence occurs:
-
The GCS detects the node failure (via missed heartbeats) and marks all objects stored on that node as lost.
-
Any task that was waiting for one of those lost objects cannot proceed. But rather than failing permanently, the local scheduler managing that task examines the lineage recorded in the GCS to determine how the lost object was originally produced.
-
The scheduler submits a reconstruction task—a re-execution of the original producing task with the same inputs. If those inputs are also lost (because they were on the same failed node), the reconstruction cascades: the scheduler recursively reconstructs the inputs' producers until it reaches objects that are still available somewhere in the cluster (either on surviving nodes or because they can be recomputed from the driver's original inputs).
-
The reconstructed objects are written to the object stores of surviving nodes, and the waiting tasks can now proceed.
Figure 11a demonstrates this in action: a workload of linear chains of 100ms tasks is submitted, and nodes are forcibly removed at 25s, 50s, and 100s. As nodes are removed, the system re-executes lost tasks (shown in the "re-executed tasks" curve), maintaining overall per-node throughput.
Why tasks are trivially reconstructible. The idempotence assumption—that tasks are stateless and deterministic given their inputs—is what makes reconstruction simple. Because re-executing a task with the same inputs must produce the same output, reconstructing a lost object is guaranteed to produce the correct value. This is why the paper specifies that remote functions "are expected to be stateless and side-effect free" (Section 3.1).
Actor reconstruction (Figure 11b). Actors, being stateful, cannot be trivially reconstructed by re-execution—if an actor has been running for hours and has processed thousands of method invocations, replaying all of them from scratch would be prohibitively expensive. Ray's solution is user-defined checkpointing: the actor developer provides a method that serializes the actor's current state into an immutable object. Periodically, the system invokes this checkpoint method and stores the resulting object in the distributed object store, recording a stateful edge from the checkpoint to the next method invocation in the GCS lineage.
When an actor's node fails:
-
The GCS detects the failure and marks the actor as lost.
-
The system locates the most recent checkpoint object for that actor (which, being immutable and stored in the object store, may be replicated on multiple nodes).
-
A new actor instance is created on a surviving node and initialized from the checkpoint.
-
All method invocations that occurred after the checkpoint are re-executed in order (following the stateful edges in the lineage) to bring the actor's state up to date.
The key insight is that stateful edges capture the order of method invocations, making the replay deterministic. Because each method call is logged in the GCS with its arguments (which are immutable objects and thus reconstructible), the sequence of post-checkpoint operations can be replayed exactly.
Figure 11b demonstrates the benefit of checkpointing: with checkpoints, recovering 400 actors (out of 2000) after killing 2 of 10 nodes requires replaying only 500 methods total. Without checkpoints, the entire history of those 400 actors would need to be replayed—approximately 10,000 method invocations. The checkpoint bounds reconstruction time to the delta since the last checkpoint, trading off checkpointing overhead against recovery time.
Exactly-once semantics. The combination of lineage logging and immutable objects provides exactly-once execution semantics for both tasks and actor methods. Each remote function call and each actor method invocation is logged in the GCS before execution. If a worker or actor process crashes mid-execution, the system can detect that the invocation was logged but never completed, and resubmit it. Because arguments are immutable and the lineage is recorded, the resubmission will receive the same inputs and produce the same output. The paper explicitly contrasts this with actor frameworks: "Orleans provides at-least-once and Akka provides at-most-once semantics. In contrast, Ray provides transparent fault tolerance and exactly-once semantics" (Section 6).
The practical argument for fault tolerance in AI workloads. The paper's Section 7 addresses a natural objection: since AI algorithms are statistical and can tolerate some noise, isn't fault tolerance overkill? The paper offers three counterarguments:
-
Simplified development: "the ability to ignore failures makes applications much easier to write and reason about." The developer doesn't need to add retry logic, dead-worker detection, or partial-result handling to their RL algorithm—the framework handles failure transparently.
-
Debugging via deterministic replay: "our particular implementation of fault tolerance via deterministic replay dramatically simplifies debugging as it allows us to easily reproduce most errors." Since lineage records the exact sequence of operations that led to a failure, developers can replay that sequence deterministically to reproduce and diagnose bugs—a capability especially valuable for stochastic AI algorithms that are "notoriously hard to debug."
-
Cost savings via spot instances: "fault tolerance helps save money since it allows us to run on cheap resources like spot instances on AWS." The paper quantifies this: for the PPO application, combining fault tolerance (enabling spot instance usage, ~4× cheaper) with heterogeneity-aware scheduling (enabling CPU-only nodes for simulation tasks) together yield an 18× cost reduction compared to an MPI implementation running on on-demand instances with homogeneous hardware (Section 5.3.2).
End-to-End Walkthrough: The add(a, b) Example
The paper's Figure 7 provides a detailed step-by-step walkthrough of a single remote function invocation, making the component interactions concrete. I will trace the full lifecycle: task submission, scheduling, argument resolution, execution, and result retrieval, connecting each step to the architectural components described above.
Setup (step 0, both panels): When the driver starts, any function decorated with @ray.remote—in this case, add(a, b)—is automatically registered with the GCS. Its definition is written to the Function Table, and workers that start later retrieve it from there. The two argument objects a and b are already stored in the object stores of nodes N1 and N2, respectively, meaning the Object Table has entries mapping ida → [N1] and idb → [N2].
Task submission and scheduling (Figure 7a, steps 1–4):
-
Step 1 (bottom-up submission): The driver on N1 calls
add.remote(a, b). This does not executeaddimmediately; instead, the driver submits the task to N1's local scheduler. The call returns a futureidcimmediately—the driver can continue executing while the task is being processed. -
Step 2 (forwarding to global scheduler): N1's local scheduler examines the task. It could choose to schedule
addlocally on N1—after all, argumentais already on N1. However, in this example, N1's local scheduler decides (for whatever reason—perhaps its queue is full, or it lacks resources) to forward the task to a global scheduler. -
Step 3 (argument location lookup): The global scheduler needs to know where
add's arguments are located to make a placement decision. It queries the GCS's Object Table for the locations ofidaandidb. The GCS responds:idais on N1,idbis on N2. -
Step 4 (placement decision): The global scheduler applies its heuristic. It evaluates candidate nodes—in this case, N1 holds
a, N2 holdsb, and perhaps other nodes hold neither. It selects N2 (the node holdingb) and sends the scheduling decision to N2's local scheduler.
Why N2 rather than N1? The paper doesn't specify which argument is larger or what the load situation is, but the general principle is that the global scheduler prefers nodes that hold inputs to minimize transfer. Here, N2 is selected, meaning a will need to be transferred to N2 before execution.
Argument resolution and execution (Figure 7a, steps 5–9):
-
Step 5 (local availability check): N2's local scheduler receives the
addtask. Before dispatching it to a worker, it checks whether all arguments—aandb—are available in N2's local object store. It findsb(sinceidbwas originally on N2), butais missing. -
Step 6 (locating missing arguments): The local scheduler queries the GCS's Object Table to determine where
ais stored. The response: N1. -
Step 7 (data replication): N2's object store initiates a transfer of object
afrom N1's object store. For large objects, this transfer would be striped across multiple TCP connections for throughput. Once the transfer completes,ais present in N2's shared-memory object store, and the Object Table is updated to include N2 as a location forida. -
Step 8 (task dispatch): With all arguments now local, N2's local scheduler dispatches the
addtask to an available worker on N2. -
Step 9 (zero-copy execution): The worker accesses arguments
aandbvia shared memory—no deserialization, no copying—executesadd, and writes the resultcto the local object store (again via shared memory).
Result retrieval (Figure 7b, steps 1–7):
Meanwhile, back on N1, the driver has called ray.get(idc) to retrieve the result:
-
Step 1 (local check): The driver (or its local object store) checks whether
cis present in N1's object store. It isn't—N1 hasn't heard aboutcyet. -
Step 2 (registering a callback): N1's object store queries the GCS's Object Table for
idc. At this moment,idchas no entry in the Object Table—theaddtask on N2 hasn't producedcyet, so no location has been registered. Rather than polling (which would waste resources), N1's object store registers a callback with the Object Table: "notify me whenidc's entry is created." -
Step 3 (result production on N2): The
addtask completes on N2, and the worker writes resultcto N2's local object store. -
Step 4 (GCS update): N2's object store creates an entry for
idcin the GCS's Object Table, recording thatcis available on N2. -
Step 5 (callback triggers): The GCS, seeing the new entry for
idc, triggers the callback that N1 registered. It notifies N1's object store: "cis available on N2." -
Step 6 (replication to N1): N1's object store initiates a transfer of
cfrom N2's object store. The Object Table is updated to include N1 as an additional location foridc. Now both N1 and N2 have copies ofc. -
Step 7 (return to driver): With
cnow in N1's local object store,ray.get(idc)can return the value to the driver. The call that began when the driver submittedadd.remote(a, b)is finally resolved.
Why so many RPCs? The paper acknowledges that "this example involves a large number of RPCs" but argues that "in many cases this number is much smaller" for three reasons (Section 4.3):
-
Most tasks are scheduled locally—the local scheduler handles them without involving the global scheduler at all. Steps 2–4 (global scheduling) are skipped.
-
GCS replies (Object Table lookups, Function Table lookups) are cached by local and global schedulers. If the same object location is queried repeatedly (as happens when many tasks read the same model parameters), only the first query goes to the GCS; subsequent queries hit the cache.
-
For tasks whose inputs are already local (the common case in data-parallel workloads where tasks are placed on nodes holding their data), steps 6–7 (argument replication) are skipped.
The walkthrough reveals what the system design abstractly enables: the GCS is queried for metadata (object locations, function definitions), but never sits on the critical path for data transfer itself. The global scheduler makes placement decisions, but most tasks bypass it entirely. The object store handles data movement via direct node-to-node transfers, without scheduler involvement. This separation of control (GCS + schedulers) from data (object store + workers) is what allows each component to scale independently while maintaining consistency through the GCS's authoritative state.
Summary of Key Design Choices and Their Justifications
-
Unified task + actor model rather than tasks-only (CIEL) or actors-only (Orleans): RL workloads inherently require both—stateless simulation tasks that benefit from locality and load balancing, and stateful training/serving processes that benefit from efficient fine-grained updates. Forcing one paradigm to simulate the other imposes serialization overhead and breaks the programming model.
-
Dynamic task graph with nested invocations rather than static DAGs (TensorFlow): RL computation is inherently data-dependent—the results of simulations determine future computation. A static graph cannot express this without awkward control-flow workarounds.
-
Decoupled GCS rather than embedding metadata in the scheduler: enables the scheduler and object store to scale independently, removes the scheduler from the data transfer path (critical for allreduce latency, Figure 12b), and provides a single point of truth for lineage-based fault tolerance.
-
Sharded, chain-replicated GCS rather than a single Redis instance: scaling lineage capacity and throughput with cluster size, maintaining fault tolerance with sub-30ms reconfiguration latency (Figure 10a).
-
Bottom-up scheduling rather than purely centralized or purely decentralized: local-first scheduling exploits data locality and keeps the common case fast; global scheduling handles load balancing and resource heterogeneity when local scheduling cannot.
-
Shared-memory object store with Apache Arrow rather than process-local memory or a separate storage service: zero-copy data sharing between colocated tasks eliminates serialization overhead for fine-grained computation; Arrow provides standardized, efficient in-memory representations.
-
Immutable, non-distributed objects rather than distributed objects or mutable state in the object store: immutability eliminates consistency protocols; "fits on one node" constraint simplifies the object store to a cache.
-
Lineage-based reconstruction with user-defined actor checkpoints rather than replication-based fault tolerance: lineage storage is cheaper than full replication (only metadata is stored, not data copies); checkpointing bounds actor recovery time to the post-checkpoint delta.
-
Exactly-once semantics via GCS logging + immutable arguments rather than at-least-once or at-most-once: the idempotence of tasks on immutable inputs guarantees that recomputation produces correct results; the GCS's invocation log ensures that every invocation is either completed or detectably incomplete.
4. Key Insights and Innovations
Innovation 1: The Inversion of the Centralized-Scheduler Orthodoxy Through Control-State Decoupling
The dominant architectural assumption in cluster computing frameworks prior to Ray was that the scheduler—which decides where work runs—should also be the system's brain, maintaining the authoritative record of what data exists where, what tasks are running, and what lineage connects outputs to their producers. Spark's driver, CIEL's master, Dryad's job manager, Dask's centralized scheduler—all of these designs couple scheduling decisions with metadata storage. For coarse-grained batch workloads, this coupling was not merely acceptable but natural: the scheduler's bookkeeping overhead was negligible compared to task durations measured in seconds or minutes, and centralizing metadata simplified consistency and fault tolerance.
The paper's most fundamental intellectual move is to identify this coupling as the root cause of a scaling ceiling that makes fine-grained, dynamic workloads—specifically the RL training-simulation-serving loop—impossible to support efficiently in existing frameworks. The insight is not merely that centralized schedulers are slow (this was known), but that the coupling of metadata from scheduling creates two distinct, compounding bottlenecks that cannot be patched away by optimizing the scheduler alone.
The first bottleneck is lineage scalability. RL workloads generate millions of fine-grained tasks, each producing intermediate objects that must be tracked for fault tolerance. Centralizing this lineage on a single driver/master node—as Spark and CIEL do—is viable when tasks number in the hundreds. When tasks number in the millions and are generated at over a million per second, the driver's memory and processing capacity become the hard limit on application scale. The paper makes this concrete through the GCS flushing experiment (Figure 10b): 50 million sequential no-op tasks would exhaust memory without periodic flushing, even with Ray's deliberately decoupled architecture. In a centralized design, this would manifest as outright failure well before reaching such scales.
The second bottleneck is more subtle and constitutes the paper's key diagnostic contribution: when the scheduler also stores object location metadata, every data transfer decision must transit the scheduler. The paper demonstrates this empirically in Figure 12b. An allreduce operation across 16 nodes involves 32 rounds of 16 tasks each. With a centralized scheduler achieving 3,000 tasks/second (Dask's reported maximum), each round incurs roughly 5ms of scheduling delay—a round-trip to the scheduler to discover where each object lives. Across 32 rounds, this adds up to approximately 160ms of pure scheduling overhead, nearly doubling the 200ms completion time that Ray achieves by allowing object stores to query the GCS's Object Table directly. This is not a hypothetical: the paper's scheduler-ablation experiment (Figure 12b) shows that injecting just 1ms of artificial scheduling latency degrades allreduce throughput by a measurable amount, and 10ms nearly doubles it.
The architectural innovation that follows from this diagnosis is the Global Control Store (GCS)—a sharded, chain-replicated key-value store that holds all system control state (object locations, task statuses, function definitions, event logs) while keeping every other component stateless. The GCS is not merely a distributed database bolted onto a scheduler; it represents a fundamental rethinking of the control plane/data plane separation. In Ray's architecture, the schedulers (local and global) are pure decision engines that read state from the GCS and write decisions back to it, but never sit on the critical path for data transfer. The object store directly queries the GCS for object locations and initiates peer-to-peer transfers without scheduler involvement.
This decoupling is what the paper means when it claims the GCS "enables every component in the system to be stateless" (Section 4.2.1), and it is what enables the three scalability properties that would be mutually contradictory in a coupled design: (1) millisecond scheduling latency (because local schedulers handle the common case without touching the GCS), (2) linear scalability to 1.8M tasks/second (because GCS sharding and scheduler replication allow both to scale independently), and (3) transparent lineage-based fault tolerance (because the GCS maintains the authoritative lineage that any component can read on restart).
The paper explicitly frames this as a design principle it expects to generalize: "centralizing control state will be a key design component of future distributed systems" (Section 7). This is a bold claim—the field has spent decades decentralizing state for scalability—but the paper's argument is that control state (metadata about what exists and what happened) has fundamentally different access patterns and consistency requirements than application state (the actual data). Control state is small relative to application data, heavily read-dominated, and requires strong consistency for correctness. These properties make it amenable to a logically centralized, physically sharded store, while application state demands the distributed, peer-to-peer data plane that Ray's object store provides. The paper does not prove this generalization—it demonstrates it for one class of workloads—but the conceptual framework it establishes is the contribution, not the specific Redis-based implementation.
This innovation is fundamental, not incremental, because it overturns the default architecture that had been carried forward from batch processing systems through task-parallel systems to deep learning frameworks. It identifies a structural coupling as the limiting factor and proposes a concrete, empirically-validated alternative. The improvement in allreduce performance over OpenMPI (Figure 12a), the linear scalability curve (Figure 8b), and the sub-30ms GCS reconfiguration latency (Figure 10a) are not independent results—they are consequences of this single architectural decision to decouple truth from execution.
Innovation 2: Unifying Task-Parallel and Actor Abstractions Under a Single Lineage-Based Fault Tolerance Model
Prior to Ray, task-parallel systems (CIEL, Dask, Spark) and actor systems (Orleans, Akka, Erlang) existed in separate worlds, with fundamentally different fault tolerance mechanisms. Task-parallel systems achieved fault tolerance through lineage—re-executing lost tasks from their recorded dependencies—because tasks are stateless and deterministic. Actor systems required explicit developer checkpointing because actors are stateful and their execution history is too long to replay from scratch. This separation was treated as inherent: the fault tolerance model was determined by the computation model, and choosing one meant accepting the recovery characteristics of that model.
Ray's insight is that both models can be unified under lineage-based reconstruction by representing actor state transitions as explicit edges in the same dependency graph that tracks task relationships. The mechanism is the stateful edge (Section 3.2): a directed edge connecting successive method invocations on the same actor, encoding the ordering constraint that method must execute after because it reads the state that wrote. By adding this edge type to the computation graph—alongside data edges (producer-consumer dependencies) and control edges (parent-child nesting relationships)—Ray embeds actors in "an otherwise stateless task graph" (Section 3.2).
The intellectual move here is recognizing that statefulness is a special case of data dependency, not a fundamentally different kind of computation. An actor's mutable state is, from the graph's perspective, an implicit data flow: each method reads the state produced by the previous method. By making this implicit dependency explicit as a stateful edge, the same reconstruction algorithm that replays lost stateless tasks can replay lost actor method invocations—just follow the stateful edges backward to the last checkpoint, reinitialize, and replay forward.
This unification has consequences beyond implementation convenience. It provides exactly-once execution semantics uniformly across tasks and actors—a guarantee that neither actor frameworks (which offer at-least-once or at-most-once) nor task-parallel systems (which offer exactly-once only for tasks) could provide for mixed workloads. The paper makes this explicit: "Ray provides transparent fault tolerance and exactly-once semantics, as each method call is logged in the GCS and both arguments and results are immutable" (Section 6).
More significantly, the unification enables transparent actor recovery without developer effort for the common case. The paper's actor reconstruction experiment (Figure 11b) shows that when 2 of 10 nodes are killed—taking 400 of 2000 actors with them—the system automatically reconstructs the lost actors from their last checkpoint and replays post-checkpoint method invocations. The developer writes a checkpoint function, and the framework handles everything else: detecting the failure, locating the checkpoint, reinstantiating the actor, replaying the method invocation sequence. In Orleans, the developer would need to manually restore state from persisted checkpoints and handle the possibility of duplicate or lost messages. In Akka, the developer would need to implement at-least-once delivery and handle deduplication.
The practical significance is crystallized in the paper's economic argument for fault tolerance (Section 7): the combination of transparent recovery and resource-aware scheduling yields an 18× cost reduction for PPO compared to an MPI implementation on on-demand instances. This number is not solely attributable to the unified fault tolerance model—it also reflects spot instance pricing and heterogeneous scheduling—but the fault tolerance is what makes spot instances viable in the first place. An MPI job running on spot instances that cannot transparently handle node preemption will either fail outright or require complex application-level checkpointing logic. Ray's unified lineage model makes preemption a non-event from the developer's perspective.
This innovation is fundamental within its domain—it bridges two previously separate programming models under a single fault tolerance mechanism—but it builds on established lineage concepts from dataflow systems (Spark, CIEL) and applies them to a new context. The key contribution is the recognition that stateful edges are sufficient to extend lineage-based recovery to stateful computation, plus the empirical demonstration that this extension works at scale with recovery times bounded by checkpoint frequency (Figure 11b showing 500 vs. 10,000 re-executions).
Innovation 3: Demonstrating That a General-Purpose Orchestration Layer Can Match or Exceed Specialized Systems for RL Workloads
The prevailing assumption in distributed systems for machine learning—visible in the proliferation of one-off systems cited in the paper's introduction—was that the tight coupling of simulation, training, and serving in RL required purpose-built infrastructure. Each new algorithm (ES, PPO, A3C, DQN, AlphaGo) spawned its own distributed system because general-purpose frameworks imposed unacceptable overhead in either latency, throughput, or programming model flexibility. The paper's Section 2 documents this assumption explicitly: "researchers and practitioners today build one-off systems for specialized RL applications."
Ray's empirical contribution is to falsify this assumption—not by outperforming every specialized system on every metric (it doesn't), but by demonstrating that a single, general-purpose framework can match or exceed specialized systems across all three RL workloads (training, serving, simulation) simultaneously, without workload-specific optimization at the framework level. The specialized systems achieve their performance through carefully tuned, application-specific communication protocols; Ray achieves comparable or better performance through general architectural principles (control-state decoupling, bottom-up scheduling, shared-memory object store) that apply uniformly across workloads.
The evidence is systematically presented across Section 5:
-
Distributed training (Figure 13): Ray with TensorFlow matches Horovod's performance and is within 10% of distributed TensorFlow's native replicated mode. The key enabler is not a training-specific optimization but the ability to express the same pipelining (overlapping gradient computation with network transfer) using Ray's general-purpose actor and task primitives plus a custom TensorFlow operator that writes tensors directly to Ray's object store. The scheduling overhead that would make this impossible in a centralized-scheduler framework is eliminated by the bottom-up design.
-
Embedded serving (Table 3): Ray achieves 6,200 states/second versus Clipper's 4,400 on a small-input model, and 6,900 versus 290 on a large-input model—an order-of-magnitude advantage on the latter. This is not because Ray is a better general-purpose serving system than Clipper (the paper explicitly disclaims this in Section 1), but because Ray's shared-memory object store eliminates the serialization and network overhead that Clipper's REST interface incurs for co-located serving within an RL application. The specialized system (Clipper) is optimized for a different deployment scenario (serving external clients) and imposes overhead that is unnecessary for in-cluster model serving.
-
Simulation (Table 4): Ray achieves 4.03M timesteps/second versus 2.16M for MPI/BSP on 256 cores—a 1.8× improvement. The performance gap comes not from faster simulation execution but from Ray's ability to dynamically collect results as simulations complete, rather than waiting for the slowest simulation in each round (the BSP barrier). This is a direct consequence of the
ray.wait()primitive and the dynamic task graph model, both of which are general-purpose mechanisms, not simulation-specific optimizations. -
End-to-end RL applications (Figure 14): Ray's ES implementation reaches a median solve time of 3.7 minutes on Humanoid-v1 at 8192 cores, more than 2× faster than the best published result (10 minutes), while the special-purpose ES system fails to complete at 2048 cores due to driver bottleneck. Ray's PPO implementation outperforms the optimized MPI implementation on all configurations while using fewer GPUs (at most 8 GPUs versus 1 GPU per 8 CPUs in the MPI setup) and reducing cost by 4.5× through heterogeneity-aware scheduling.
The intellectual significance of these results is not that Ray is "faster" in some absolute sense, but that the overhead of generality—long assumed to be prohibitive for RL workloads—can be made negligible through architectural decisions that are independent of the specific RL algorithm. The bottom-up scheduler, shared-memory object store, and decoupled GCS do not know about policy gradients, evolution strategies, or Q-learning. They provide a substrate on which all of these algorithms can be expressed efficiently, and the empirical results show that the substrate's overhead is low enough that algorithm-level optimizations (hierarchical aggregation for ES, pipelined gradient transfer for training) can be expressed at the application level and still match hand-tuned implementations.
This is an empirical refutation, not a theoretical advance. It changes the conversation from "which specialized system should I build for my RL algorithm?" to "can I express my RL algorithm in Ray's primitives and get acceptable performance without building infrastructure?" For the algorithms evaluated—ES, PPO, and by extension the others listed in Section 7 (A3C, DQN, DDPG, Ape-X)—the answer is yes. The paper does not claim universality, and the limitation section of Section 8 implicitly acknowledges that algorithms with fundamentally different communication patterns might expose bottlenecks in Ray's current scheduler or object store. But for the class of RL workloads that dominated research at the time of publication, the demonstration is comprehensive.
The significance beyond raw performance is that this result changes the economics of RL research. If each new algorithm requires a custom distributed system, the systems engineering cost acts as a barrier to entry—only well-resourced labs can evaluate ideas at scale. If a general-purpose framework can achieve comparable performance, the barrier drops substantially. The paper notes that porting the ES algorithm to Ray required modifying only 7 lines of code from a serial implementation, while the reference implementation "had several hundred lines of code dedicated to a protocol for communicating tasks and data between workers" (Section 5.3.1). This is a qualitative difference in research velocity that matters independently of the absolute performance numbers.
Innovation 4: The Bottom-Up Scheduling Strategy as a Mechanism for Combining Locality and Load Balancing Without Central Bottlenecks
The scheduling literature prior to Ray presented a tension between data locality (place tasks where their inputs reside) and load balancing (distribute tasks evenly across nodes). Centralized schedulers (Spark, CIEL) achieved good locality by consulting global object location tables but at the cost of scheduling latency and throughput bottlenecks. Decentralized work-stealing schedulers (Cilk) achieved excellent load balancing through randomized task migration but ignored data locality—in a distributed setting, a stolen task might need to fetch all its inputs from remote nodes, negating the benefit of load balancing. Sparrow's decentralized design achieved low latency but made independent scheduling decisions without considering data dependencies across tasks.
Ray's bottom-up scheduler is not merely a two-level hierarchy (many systems have those); its distinctive contribution is the inversion of the scheduling decision flow. In a conventional hierarchical scheduler (e.g., Mesos), decisions flow top-down: a central allocator offers resources to frameworks, which then schedule tasks within those allocations. In Ray, decisions flow bottom-up: tasks are submitted first to the local scheduler, which only forwards them upward when local execution is undesirable. This inversion has two consequences that the paper identifies but that are worth separating conceptually.
First, locality emerges by default rather than requiring explicit optimization. Because tasks are submitted to the node where they were created, and because that node often holds the task's inputs (since those inputs were produced by previous tasks on the same node), the common case is that tasks execute where their data already resides. The global scheduler is only involved when this default fails—when the local node is overloaded or lacks required resources. This is the opposite of a centralized scheduler, which must choose to place a task near its data based on global knowledge, incurring a scheduling decision cost for every task regardless of whether the optimal placement is local.
Second, the global scheduler's workload scales sub-linearly with cluster size. In a well-configured cluster where most tasks' resource requirements are met locally, the fraction of tasks that reach the global scheduler is small. The paper does not quantify this fraction directly, but the scalability experiment (Figure 8b) shows near-perfect linear throughput scaling to 100 nodes, which implies that the global scheduler is not becoming a bottleneck as the cluster grows. If the global scheduler handled a constant fraction of all tasks, its load would grow linearly with cluster size, and linear throughput scaling would require linear scaling of global scheduler capacity. The fact that Ray achieves linear scaling with a fixed number of global schedulers (the paper doesn't specify this number, but the architecture description implies a small number of replicas) suggests that the bottom-up design successfully confines most scheduling work to the local level.
The paper contrasts this with Sparrow explicitly (Section 6): Sparrow's decentralized schedulers "make independent decisions, limiting the possible scheduling policies," and "all tasks of a job are handled by the same global scheduler," which prevents fine-grained load balancing across jobs. Canary achieves high throughput by partitioning the task graph among scheduler instances, but "does not handle dynamic computation graphs." Ray's design handles dynamic graphs, considers data locality and resource constraints, and scales horizontally—a combination that no prior scheduler achieved.
The empirical validation comes from Figures 8a and 8b. Figure 8a demonstrates the benefit of locality-aware task placement: task latency remains constant across input sizes from 100KB to 100MB when the scheduler places tasks near their data, but grows by 1-2 orders of magnitude without locality awareness. Figure 8b demonstrates that the scheduler architecture doesn't just work at small scale—it scales linearly to 1.8M tasks/second at 100 nodes, processing 100 million tasks in 54 seconds. This is not just a scheduler throughput number; it validates the architectural claim that bottom-up scheduling keeps the bottleneck out of the scheduling path.
This innovation is an architectural refinement that enables a new operating regime—specifically, the regime where tasks are fine-grained enough (millisecond durations) that scheduling overhead would dominate if every task touched a centralized scheduler, and dynamic enough (task graph evolves during execution) that static graph partitioning approaches cannot be applied. It does not introduce new scheduling theory or optimality guarantees; the paper's scheduling algorithm is a greedy heuristic with no formal analysis. But it demonstrates that the heuristic, combined with the bottom-up flow, is sufficient for the RL workloads studied, and the architecture generalizes beyond those workloads.
Innovation 5: Reconceptualizing RL Workloads as a Unified Orchestration Problem Rather Than Three Separate Systems Challenges
The paper's framing of RL workloads—training, serving, and simulation as "tightly coupled" operations requiring a single framework—is not itself a technical contribution. But the paper's systematic demonstration that this coupling imposes requirements that no existing system architecture can satisfy, even in principle, represents a significant reframing of the problem.
Prior to Ray, the dominant approach to building RL systems was composition: use Horovod or a parameter server for distributed training, a model server (Clipper, TensorFlow Serving) for policy serving, and a task-parallel framework or custom MPI code for simulation. The paper argues that this composition is "prohibitive in the context of RL" due to "the resulting data movement and latency between systems" (Section 2). But the deeper argument—distributed throughout the paper rather than stated in one place—is that the requirements of the coupled system are not the sum of the requirements of its parts.
Specifically, the paper identifies three cross-cutting requirements that emerge from the coupling:
-
Heterogeneity in time and resources within a single application: Simulation tasks range from milliseconds to hours; training requires GPUs while simulation typically uses CPUs; serving demands microsecond latency while training tolerates second-scale iteration times. A system that handles only one of these workloads (e.g., a training framework) doesn't need to express resource heterogeneity at the task level. A system that handles all three must allow developers to specify—at the granularity of individual tasks and actors—what resources they need, and must schedule accordingly. Ray's
@ray.remote(num_gpus=2)annotation is a simple mechanism, but its existence reflects this cross-cutting requirement. -
Dynamic, data-dependent control flow across workload boundaries: The decision of whether to continue simulation, update the policy, or serve the current policy depends on the results of previous operations. A simulation might reveal that the current policy is catastrophically bad, triggering early termination and immediate policy update—a control flow decision that crosses the simulation-training boundary. BSP systems cannot express this because they require all simulations in a round to complete before training begins. Task-parallel systems can express it, but without actors, they cannot efficiently implement the training side of the loop.
-
Unified fault tolerance across stateless and stateful components: If a simulation worker fails, the system should retry the simulation (stateless recomputation). If a parameter server actor fails, the system should restore its state from a checkpoint and replay recent updates. In a composite system, these would be handled by different mechanisms (the simulation framework retries, the training framework restores from its own checkpoint), likely with inconsistent semantics. Ray's unified lineage model handles both uniformly, providing exactly-once semantics across the entire application.
The paper's significance is that it identifies these as requirements of the orchestration layer rather than requirements that must be solved within each workload-specific subsystem. By doing so, it carves out a new category of system—the RL orchestration framework—that didn't exist before. The paper is careful to position this category as complementary to, not replacing, specialized systems: "Ray does not aim to substitute for serving systems like Clipper... Ray is not a substitute for generic data-parallel frameworks, such as Spark" (Section 1). But the category itself is new, and the paper's demonstration that a single architecture can satisfy the cross-cutting requirements validates its existence.
This innovation is conceptual and taxonomic rather than algorithmic. It doesn't introduce new mechanisms for training, serving, or simulation; it introduces the idea that these should be orchestrated by a single system with a unified programming model, and it provides the first architecture that makes this orchestration efficient enough to be practical. The subsequent adoption of Ray—the paper notes "several companies are running it in production" within roughly a year of release (Section 7)—suggests that this reconceptualization addressed a real gap that practitioners had been working around with ad-hoc solutions.
5. Experimental Analysis
Evaluation Methodology
-
Dataset. The paper evaluates on multiple microbenchmark workloads and two end-to-end reinforcement learning applications (Evolution Strategies and Proximal Policy Optimization). For the microbenchmarks, workloads are synthetic—empty tasks for scalability, random object dependencies for locality scheduling, linear chains of 100ms tasks for fault tolerance, and
Pendulum-v0from OpenAI Gym for simulation throughput. For the end-to-end RL applications, the benchmark is the Humanoid-v1 task from OpenAI Gym, with the metric being time to reach a score of 6000. For the distributed training microbenchmark, the workload is ResNet-101 training on synthetic data from the official TensorFlow benchmark suite. -
Base model(s). The experiments use Amazon Web Services (AWS) instances:
m4.16xlargeCPU instances andp3.16xlargeGPU instances unless otherwise stated. For specific experiments: the allreduce benchmark uses 16m4.16xlnodes; the training benchmark usesp3.16xlinstances with 4 GPUs per node; the ES experiments scale to 8192 cores; the PPO experiments usep2.16xlarge(GPU) andm4.16xlarge(high CPU) instances. -
Metrics. The paper evaluates across multiple dimensions:
- Task throughput: Tasks completed per second, measured for the scalability microbenchmark (Figure 8b).
- Task latency: Mean time from task submission to completion, measured for the locality scheduling experiment (Figure 8a).
- Object store throughput: Write throughput in GB/s for large objects (stripe across multiple TCP connections) and IOPS for small objects (Figure 9).
- GCS latency: Read and write latencies in microseconds from a client submitting tasks, measured during chain replication reconfiguration (Figure 10a).
- Allreduce iteration time: Mean execution time in milliseconds for ring allreduce across 16 nodes at varying object sizes (Figure 12a).
- Training throughput: Images per second for ResNet-101 training (Figure 13).
- Serving throughput: States per second processed by a model server queried by co-located clients (Table 3).
- Simulation throughput: Timesteps per second for Pendulum-v0 simulations (Table 4).
- RL time-to-solution: Mean and median time in minutes to reach a score of 6000 on Humanoid-v1 (Figures 14a, 14b).
-
Baselines. The paper compares against both general-purpose and specialized systems:
- OpenMPI (v1.10): A popular MPI implementation, used as baseline for allreduce (Figure 12a), simulation (Table 4), and the PPO reference implementation (Figure 14b).
- Horovod + TensorFlow: State-of-the-art distributed training system, compared for ResNet-101 training throughput (Figure 13).
- Distributed TensorFlow (replicated mode): TensorFlow's native distributed training, same benchmark (Figure 13).
- Clipper: A dedicated low-latency model serving system, compared for embedded serving throughput (Table 3).
- Reference ES implementation: The special-purpose system from Salimans et al. (2017) built specifically for Evolution Strategies, compared for time-to-solve on Humanoid-v1 (Figure 14a).
- MPI PPO implementation: The highly-optimized reference implementation from OpenAI Baselines (Dhariwal et al., 2017) that uses OpenMPI communication primitives, compared for time-to-solve on Humanoid-v1 (Figure 14b).
- Bulk-synchronous MPI (simulation): An MPI implementation that submits 3n parallel simulation runs on n cores in 3 rounds with global barriers between rounds, used to represent BSP-style execution (Table 4).
-
Generation budget / compute accounting. For the scalability microbenchmark, the "generation budget" is the number of empty no-op tasks submitted in an embarrassingly parallel pattern. Throughput is measured as tasks completed per second across increasing cluster sizes (10-100 nodes). For fault tolerance experiments, tasks are 100ms duration with linear dependencies. For the allreduce benchmark, the budget is measured in objects of varying sizes (10MB, 100MB, 1GB) across 16 nodes, with iteration time as the metric. For RL applications, the budget is measured in CPU cores (256 to 8192 for ES; configurations from 8 CPUs × 1 GPU to 512 CPUs × 64 GPUs for PPO) and wall-clock time to reach the target score.
-
Cross-validation / statistical protocol. The paper does not report cross-validation for its microbenchmarks. Results are presented as single-run measurements (Figure 8a) or averaged over 5 runs (Figure 9). For the ES experiments, the paper reports mean time to solve with observed variance (Figure 14a shows that "ES is faster than PPO on this benchmark, but shows greater runtime variance"). For the training benchmark, the paper notes "some measurement deviations from previously reported, likely due to hardware differences and recent TensorFlow performance improvements" (Figure 13 caption).
Main Quantitative Results
Locality-Aware Task Placement
Headline result: When tasks are scheduled with locality awareness (placed on nodes that already hold their input data), task latency remains constant across input sizes from 100KB to 100MB. When tasks are scheduled without locality awareness (as is the case for actor methods, which must execute on the actor's assigned node), latency increases by 1-2 orders of magnitude for inputs of 10-100MB.
Figure 8a demonstrates this with 1000 tasks, each with a random object dependency, scheduled onto one of two nodes. The locality-aware policy keeps mean task latency essentially flat (~10^-4 seconds for small objects, ~10^-3 seconds for large objects), while the locality-unaware policy shows latency growing from ~10^-4 seconds at 100KB to ~10^-1 seconds at 100MB—roughly 1000× increase.
This quantifies the fundamental tradeoff between tasks and actors stated in Table 2: tasks provide "support for object locality" because they can be dynamically placed near their data, while actors provide "poor locality support" because once instantiated on a node, they cannot move, forcing data to travel to them.
End-to-End Scalability
Headline result: Ray achieves near-perfect linear scalability on an embarrassingly parallel workload of empty tasks, exceeding 1 million tasks per second at 60 nodes and continuing to scale linearly beyond 1.8 million tasks per second at 100 nodes. At the rightmost datapoint, Ray processes 100 million tasks in 54 seconds, with minimum variability (Figure 8b).
The x-axis increases cluster size from 10 to 60 nodes (with x ∈ {70, 80, 90} omitted due to cost), and the y-axis shows throughput in millions of tasks per second. The curve is near-perfectly linear through the entire range tested. The paper notes that "increasing task duration reduces throughput proportionally to mean task duration, but the overall scalability remains linear," though this is a stated claim without a dedicated figure showing variable-duration tasks.
The paper acknowledges a limitation: "many realistic workloads may exhibit more limited scalability due to object dependencies and inherent limits to application parallelism." This experiment demonstrates the scalability of the architecture itself (scheduler, GCS, object store) under ideal conditions with no data dependencies, not the scalability of realistic RL workloads.
Object Store Performance
Headline result: From a single client, the object store achieves write throughput exceeding 15 GB/s for large objects (using up to 8 threads to stripe copies for objects >0.5MB) and 18,000 IOPS for small objects (using 1 thread). The crossover point where multi-threading becomes beneficial is 0.5MB (Figure 9).
The bar plot in Figure 9 shows throughput scaling with thread count (1, 2, 4, 8, 16 threads) across object sizes from 1KB to 1GB. For small objects (1KB-10KB), throughput is IOPS-limited at roughly 10,000-18,000 operations/second, and adding threads doesn't help because the overheads are in serialization and IPC between client and object store. For large objects (100MB-1GB), throughput scales with thread count up to ~15 GB/s, after which memcpy dominates object creation time.
This microbenchmark establishes the per-node limits: at 18,000 IOPS, a single node can support approximately 9,000 tasks per second if each task involves at least one write (and potentially multiple reads). This is consistent with the cluster-level throughput—100 nodes × 18,000 IOPS/node ≈ 1.8M tasks/second—suggesting the object store IOPs ceiling is roughly in line with the scheduler throughput ceiling.
GCS Fault Tolerance
Headline result: During chain replication reconfiguration (killing a chain member at t ≈ 4.2s, then adding a new member that initiates state transfer and restores 2-way replication), the maximum client-observed latency remains under 30ms for both reads and writes to the GCS (Figure 10a).
The timeline in Figure 10a shows read and write latencies from the perspective of a client submitting tasks. The chain starts with 2 replicas. At t ≈ 4.2s, a chain member is killed; immediately after, a new chain member joins, initiates state transfer, and restores the chain to 2-way replication. The client sends requests as fast as it can, with at most one in-flight request at a time. Failures are reported to the chain master either from the client (explicit errors or timeouts despite retries) or from any server in the chain. The sub-30ms maximum latency includes both failure detection and recovery delays.
The paper notes that this is a lightweight chain replication implementation built on top of Redis, and the 30ms bound is what makes the GCS viable as the always-available source of truth for scheduling decisions.
Headline result: With periodic GCS flushing to disk, memory consumption is capped at a user-configurable level even for workloads of 50 million sequential no-op tasks. Without flushing, the GCS exhausts available memory and the workload fails to complete within a predetermined duration (Figure 10b).
The elapsed time axis extends to 60,000 seconds (~16.7 hours). Without GCS flushing, memory grows linearly with task count and reaches maximum capacity, causing the workload to stall (marked by the red cross). With aggressive flushing, consumed memory is "kept as low as possible" and the workload completes successfully.
This experiment validates that lineage storage is manageable for long-running applications—a practical concern that could otherwise make the lineage-based fault tolerance model infeasible for production deployments.
Recovering from Task Failures
Headline result: When nodes are forcibly removed at 25s, 50s, and 100s during a workload of linear chains of 100ms tasks, Ray transparently reconstructs lost dependencies. Overall per-node throughput remains stable throughout, with the re-executed tasks curve showing the additional work needed for reconstruction (Figure 11a).
The workload runs on m4.xlarge instances. As nodes are removed (dotted line), the local schedulers reconstruct previous results in the dependency chain to continue execution. The "original tasks" throughput curve dips when nodes are removed but recovers, and the "re-executed tasks" curve spikes during reconstruction periods. The number of nodes (shown on a secondary axis) decreases at each removal point and increases again when nodes are added back.
The key claim is that the throughput per surviving node remains stable—the system does not collapse under the reconstruction load but rather redistributes work and recovers the lost state.
Recovering from Actor Failures
Headline result: When 2 of 10 nodes are killed at t = 200s, causing 400 of 2000 actors to be lost, Ray recovers the actors from their last checkpoint and replays post-checkpoint method invocations within ~70 seconds (t = 200-270s). With checkpointing, only 500 methods need to be re-executed versus 10,000 re-executions without checkpointing (Figure 11b).
The throughput plot in Figure 11b shows three curves: original tasks, re-executed tasks, and checkpoint tasks. At t = 200s, the original task throughput drops sharply as 400 actors become unavailable, and the re-executed task throughput rises as the system replays method invocations to reconstruct the lost actors' state. The checkpoint tasks curve shows minimal overhead during normal operation.
The experiment demonstrates that user-defined checkpoint functions can bound actor reconstruction time to the delta since the last checkpoint—a 20× reduction in re-execution work (500 vs. 10,000 methods). The paper acknowledges this is not optimal: "we hope to further reduce actor reconstruction time, e.g., by allowing users to annotate methods that do not mutate state," which would eliminate replay for read-only method invocations.
Allreduce Performance
Headline result: Ray's ring allreduce implementation completes allreduce across 16 nodes on 100MB in ~200ms and on 1GB in ~1200ms, surprisingly outperforming OpenMPI (v1.10) by 1.5× and 2× respectively (Figure 12a). For smaller objects (10MB), OpenMPI outperforms Ray by switching to a lower-overhead algorithm.
The paper attributes Ray's advantage at large object sizes to its use of multiple threads for network transfers, "taking full advantage of the 25Gbps connection between nodes on AWS, whereas OpenMPI sequentially sends and receives data on a single thread." The Ray* variant (restricted to 1 thread for sending and 1 thread for receiving) is shown as an intermediate bar, confirming that multi-threading is the source of the advantage.
Headline result: Ray's scheduler performance is critical to allreduce. Injecting artificial task execution delays of just 1-10ms degrades allreduce completion time by nearly 2× (Figure 12b). The paper argues that "systems with centralized schedulers like Spark and CIEL typically have scheduler overheads in the tens of milliseconds, making such workloads impractical."
Figure 12b shows allreduce iteration time at 16 nodes and 100MB with injected delays of +0, +1, +5, and +10ms. The baseline (+0ms) is ~200ms; +1ms increases to ~250ms; +10ms increases to ~400ms. The paper also notes that "scheduler throughput also becomes a bottleneck since the number of tasks required by ring reduce scales quadratically with the number of participants"—each round involves 16 tasks, and 32 rounds require 512 tasks total.
Distributed Training
Headline result: Ray with TensorFlow matches Horovod's training throughput and is within 10% of distributed TensorFlow (in replicated mode) when training ResNet-101 on synthetic data. The experiment scales from 4 to 64 GPUs (V100), and Ray's implementation achieves approximately 6,700 images/second at 64 GPUs versus Horovod's approximately 6,700 and distributed TensorFlow's approximately 7,300 (Figure 13).
The Ray implementation uses the actor abstraction to represent model replicas, with weight synchronization via allreduce or parameter server, both implemented on top of the Ray API. A key optimization is pipelining gradient computation, transfer, and summation within a single iteration: "to overlap GPU computation with network transfer, we use a custom TensorFlow operator to write tensors directly to Ray's object store."
The paper explicitly states that the comparison is against "TensorFlow-based systems to accurately measure the overhead imposed by Ray, rather than differences between the deep learning frameworks themselves." The models and synthetic data generators are identical across all three systems. Some measurement deviations from previously reported numbers are attributed to "hardware differences and recent TensorFlow performance improvements."
Embedded Serving
Headline result: For an embedded serving workload where client and server processes are co-located on the same machine, Ray achieves 6,200 states/second vs. Clipper's 4,400 for a small fully-connected network (5ms evaluation, 4KB input batches of 64), and 6,900 states/second vs. Clipper's 290 for a residual network (10ms evaluation, 100KB input batches of 64)—an order-of-magnitude advantage on the larger-input model (Table 3).
The paper attributes Ray's advantage to "low-overhead serialization and shared memory abstractions" versus Clipper's REST interface. The critical qualification is that this comparison is for embedded serving—where the policy is served to simulators running within the same Ray application—not for serving external clients, which is Clipper's target use case. The paper explicitly states this scope distinction in Section 5.2.2: "Ray focuses primarily on the embedded serving of models to simulators running within the same dynamic task graph... In contrast, systems like Clipper focus on serving predictions to external clients."
Simulation
Headline result: On the Pendulum-v0 simulator from OpenAI Gym, Ray's asynchronous task execution achieves 4.03M timesteps/second on 256 CPUs versus 2.16M for bulk-synchronous MPI—a 1.8× throughput improvement. The gap grows with scale: at 16 CPUs, the comparison is 290K (Ray) vs. 208K (MPI); at 1 CPU, they are essentially identical (22.3K vs. 22.6K) (Table 4).
The MPI implementation submits 3n parallel simulation runs on n cores in 3 rounds, with a global barrier between rounds—simulating BSP execution. The Ray program issues the same 3n tasks while concurrently gathering simulation results back to the driver, allowing completed simulations to be processed immediately rather than waiting for the slowest simulation in each round. The paper acknowledges that "experts can use MPI's asynchronous primitives to get around barriers—at the expense of increased program complexity"—the comparison is between Ray's natural programming model and the simplest correct MPI implementation, not an MPI implementation with equivalent engineering effort.
Evolution Strategies (ES) Application
Headline result: Ray's ES implementation scales to 8192 cores, achieving a median time-to-solve of 3.7 minutes on Humanoid-v1—more than twice as fast as the best published result (10 minutes). Doubling the cores available yields an average completion time speedup of 1.6×. The special-purpose reference system fails to complete at 2048 cores because "the work in the system exceeds the processing capacity of the application driver" (Figure 14a).
The reference implementation relies on Redis for messaging and low-level multiprocessing libraries for data-sharing, with a centralized driver that broadcasts new policies to workers and aggregates results. At 2048 cores, this driver becomes the bottleneck. Ray's implementation avoids this bottleneck by using an aggregation tree of actors—a hierarchical reduction that distributes the aggregation load across multiple processes. The paper notes that implementing the aggregation tree "was easy to realize with Ray's support for nested tasks and actors," while the reference implementation "had several hundred lines of code dedicated to a protocol for communicating tasks and data between workers, and would require further engineering to support optimizations like hierarchical aggregation."
The initial parallelization of the serial ES implementation in Ray required modifying only 7 lines of code.
Proximal Policy Optimization (PPO) Application
Headline result: The Ray PPO implementation outperforms the optimized MPI implementation in all experiments (configurations from 8 CPUs × 1 GPU to 512 CPUs × 64 GPUs), while using fewer GPUs—at most 8 GPUs versus the MPI implementation's requirement of 1 GPU for every 8 CPUs (Figure 14b).
The key enabler is Ray's heterogeneity-aware scheduling. The MPI implementation exhibits symmetric architectures where all processes run the same code and require identical resources, forcing GPU allocation even for tasks that don't need GPUs. Ray allows the user to "express resource requirements at the granularity of a task or actor," meaning CPU-only simulation tasks can be scheduled on cheaper high-CPU instances while GPU training tasks run on GPU instances. The Ray implementation can "leverage TensorFlow's single-process multi-GPU support and can pin objects in GPU memory when possible"—an optimization that "cannot be easily ported to MPI due to the need to asynchronously gather rollouts to a single GPU process."
The paper quantifies the economic impact: Ray's ability to handle resource heterogeneity decreased PPO's cost by a factor of 4.5× since CPU-only tasks can be scheduled on cheaper high-CPU instances. Combined with transparent fault tolerance (enabling the use of spot instances, assumed 4× cheaper than on-demand), the total cost reduction is 18× compared to the MPI implementation running on on-demand instances with homogeneous hardware.
The paper also notes that the MPI reference implementation "includes two custom implementations of PPO, one using MPI for large clusters and one that is optimized for GPUs but that is restricted to a single node. Ray allows for an implementation suitable for both scenarios."
Ablation Studies and Robustness Checks
Locality-aware vs. locality-unaware scheduling: Figure 8a directly compares task latency when the scheduler places tasks on nodes holding their input data (locality-aware) versus when it does not (as is the case for actor methods). The 1-2 order of magnitude latency difference for 10-100MB inputs validates the task-parallel model's advantage for fine-grained load balancing with large data dependencies, one of the key tradeoffs described in Table 2.
Single-threaded vs. multi-threaded object transfer in allreduce: Figure 12a includes a Ray* variant that restricts Ray to 1 thread for sending and 1 thread for receiving. Ray* performs worse than both full Ray and OpenMPI, confirming that multi-threaded network transfers are the source of Ray's allreduce advantage on large objects. The paper states this directly: "We attribute Ray's performance to its use of multiple threads for network transfers, taking full advantage of the 25Gbps connection between nodes on AWS."
Scheduler latency injection for allreduce: Figure 12b systematically varies the injected scheduling delay (+0, +1, +5, +10ms) and measures allreduce completion time at 16 nodes with 100MB objects. The near-2× degradation with +10ms of scheduling delay quantifies why centralized schedulers (which typically have tens of milliseconds of overhead) are "prohibitive" for communication-intensive primitives like allreduce. This is not a standard ablation but rather a stress test that validates a core architectural claim: that scheduler latency directly impacts data-plane performance when the scheduler is on the critical path.
GCS with vs. without periodic flushing: Figure 10b shows the GCS memory footprint for 50 million sequential no-op tasks. Without flushing, memory grows linearly until the system stalls. With flushing, memory is capped. This validates that lineage-based fault tolerance is feasible for long-running applications without unbounded memory growth—a practical concern that could otherwise make the design non-viable.
Actor reconstruction with vs. without checkpointing: Figure 11b compares the number of method re-executions needed to recover 400 actors after node failure: ~500 with checkpointing versus ~10,000 without. This quantifies the benefit of user-defined checkpoint functions and demonstrates that recovery time can be bounded to the post-checkpoint delta rather than the full actor lifetime. The experiment also shows that checkpointing overhead is minimal during normal operation (the "checkpoint tasks" curve is near-zero).
OpenMPI vs. Ray on different object sizes (allreduce): Figure 12a shows a crossover in relative performance. On 10MB objects, OpenMPI outperforms Ray by switching to a lower-overhead algorithm. On 100MB and 1GB objects, Ray outperforms OpenMPI by 1.5× and 2× respectively. The paper acknowledges this limitation: "For smaller objects, OpenMPI outperforms Ray by switching to a lower overhead algorithm, an optimization we plan to implement in the future."
Ray with TensorFlow vs. Horovod vs. Distributed TensorFlow: Figure 13 compares three systems that all use TensorFlow as the underlying deep learning framework, varying only the orchestration layer. This isolates Ray's overhead from differences in the DL framework itself, as the paper states: "We compare only against TensorFlow-based systems to accurately measure the overhead imposed by Ray, rather than differences between the deep learning frameworks themselves."
ES reference implementation at scale: The reference ES system fails to complete at 2048 cores, while Ray's implementation scales to 8192. This is not a controlled ablation but a demonstration that the reference system's centralized driver architecture creates a hard scaling limit that Ray's distributed architecture (with aggregation tree) overcomes. The specific bottleneck is described as "the work in the system exceeds the processing capacity of the application driver."
MPI PPO vs. Ray PPO at different scales: Figure 14b shows Ray PPO outperforming MPI PPO across all tested configurations (8×1, 64×8, 512×64 CPUs×GPUs). The performance gap is attributed to Ray's ability to use asymmetric architectures and pin objects in GPU memory, and the cost gap to heterogeneity-aware scheduling. The paper notes that the MPI implementation includes two separate codebases (one for distributed MPI, one for single-node GPU), while Ray requires only one implementation.
Critical Assessment
The experimental evaluation is comprehensive in scope but reveals several patterns that qualify the strength of the paper's claims.
On the claim of "scaling beyond 1.8 million tasks per second": The scalability experiment (Figure 8b) demonstrates this on empty tasks—the simplest possible workload with no data dependencies, no object creation, and no communication. The paper acknowledges this limitation explicitly: "many realistic workloads may exhibit more limited scalability due to object dependencies and inherent limits to application parallelism." This is appropriate candor. However, the claim appears in the abstract without this qualification, which could mislead a reader who doesn't reach Section 5.1's caveat. The 1.8M tasks/second figure is best understood as the architectural throughput ceiling—the maximum rate at which the scheduler, GCS, and object store can process task lifecycle events without application-level bottlenecks—rather than an achievable throughput for realistic RL workloads.
The experiment also omits data points at 70, 80, and 90 nodes "due to cost," which leaves a gap in the linearity demonstration. While the trend is clearly linear from 10 to 60 and the 100-node point confirms continued scaling, the missing intermediate points would strengthen confidence that no inflection point exists in that range.
On the claim of "better performance than existing specialized systems for several challenging reinforcement learning applications": This claim is supported for ES and PPO specifically (Figures 14a, 14b), but with important boundaries. For ES, the comparison is against the reference implementation from Salimans et al. (2017), which "failed to run beyond 1024 cores." The Ray implementation's 3.7-minute solve time at 8192 cores versus the published 10-minute result is a genuine improvement, but the reference system's failure at scale means the comparison is partly between Ray and a system that was not engineered for the same scale—Ray wins partly because it can scale further, not necessarily because it's faster at a given core count.
For PPO, the comparison is against "a highly-optimized reference implementation [5] that uses OpenMPI communication primitives." The Ray implementation outperforms MPI PPO on all configurations while using fewer GPUs. However, the MPI implementation represents a particular architectural choice (symmetric, homogeneous processes) that the paper argues is inherently limited for heterogeneous workloads. The comparison would be stronger if it included a PPO implementation that uses a parameter-server architecture (which would be more heterogeneous-friendly) rather than only the MPI variant.
On the claim of "4.5× cost reduction" for PPO: This number depends on the specific pricing differential between high-CPU and GPU instances on AWS at the time of writing. The paper cites the EC2 instance pricing page but doesn't specify exact instance types or prices. The 18× figure (4.5× from heterogeneity-aware scheduling × 4× from spot instances) is presented in Section 5.3.2 but represents an upper bound—actual savings would depend on spot instance availability, preemption rates, and the cost of checkpointing overhead during normal operation (which Figure 11b shows is minimal but not zero).
On the allreduce comparison with OpenMPI: Ray's 1.5-2× advantage on 100MB and 1GB objects (Figure 12a) is attributed to multi-threaded network transfers versus OpenMPI's single-threaded sequential send/receive. However, on 10MB objects, OpenMPI is faster by switching to a lower-overhead algorithm. The paper acknowledges this as a limitation and plans to implement similar optimizations. The comparison also focuses only on ring allreduce; other collective communication algorithms (recursive doubling, Rabenseifner's algorithm) might show different relative performance. The claim that Ray's architecture "enables primitives like allreduce without system modification" (Section 6) is supported, but the claim that Ray "outperforms OpenMPI" requires qualification about object size and algorithm choice.
On the embedded serving comparison with Clipper: The order-of-magnitude advantage on large-input serving (6,900 vs. 290 states/second, Table 3) is for a specific deployment scenario: co-located client and server within the same Ray application, using shared memory. Clipper uses REST and is designed for serving external clients—a fundamentally different deployment model with different requirements (authentication, model versioning, request queuing). The paper is explicit about this scope limitation in Section 5.2.2, but the abstract's claim of "better performance than existing specialized systems" could be read as a general serving performance claim, which would be misleading without this context.
On what is missing from the experimental evaluation:
-
End-to-end RL application combining training, serving, and simulation: The paper evaluates each workload in isolation (Sections 5.2.1, 5.2.2, 5.2.3) and evaluates two RL algorithms (Section 5.3), but never presents an experiment that demonstrates the tight coupling of all three workloads simultaneously—which is the paper's motivating scenario in Section 2. The ES and PPO applications involve training and simulation, but embedded serving (where the updated policy is served to simulators within the same task graph) is not explicitly evaluated in the end-to-end setting with latency measurements for the policy serving path.
-
Scalability with data dependencies: The scalability experiment (Figure 8b) uses empty tasks. An experiment showing scaling behavior with tasks that read and write objects of realistic sizes (e.g., the parameter sizes and trajectory sizes from the RL applications) would reveal whether object store bandwidth or GCS metadata throughput becomes a bottleneck before the scheduler does.
-
GCS sharding scalability: The paper states that "we were able to scale by adding more shards whenever the GCS became a bottleneck" (Section 7) but does not present an experiment showing GCS throughput as a function of shard count, nor the point at which sharding becomes necessary. The 1.8M tasks/second experiment presumably uses multiple GCS shards, but the configuration is not specified.
-
Fault tolerance under realistic RL workloads: The task reconstruction experiment (Figure 11a) uses linear chains of 100ms tasks; the actor reconstruction experiment (Figure 11b) kills 400 of 2000 actors. Neither experiment is run on an actual RL training workload where recovery would need to handle the interaction between simulation tasks (stateless, reconstructible) and training actors (stateful, checkpointed) simultaneously. The claimed 18× cost reduction depends on fault tolerance enabling spot instance usage, but no experiment demonstrates RL training completing successfully through spot instance preemption events.
-
Comparison with parameter-server architectures for training: The training benchmark (Figure 13) compares against Horovod (allreduce-based) and distributed TensorFlow (replicated mode), but not against a dedicated parameter-server system. Given that the paper identifies parameter servers as a primary use case for actors (Section 3.1), a comparison showing that Ray's parameter server implementation is competitive with dedicated parameter-server frameworks would strengthen this claim.
-
Sensitivity to task granularity: The paper argues that Ray targets "fine-grained computations" with millisecond durations, but doesn't systematically evaluate how throughput and latency change as a function of task duration. The empty-task experiment represents one extreme (near-zero duration); the 100ms task experiment represents another point. A sweep across task durations would reveal the crossover point where scheduling overhead becomes negligible relative to task execution time.
-
GCS caching effectiveness: The paper states that "GCS replies are cached by the global and local schedulers" (Section 4.3) and that this reduces RPC count in the common case, but no experiment measures cache hit rates or the performance impact of caching versus direct GCS queries for realistic RL workloads where model parameters are read repeatedly by many tasks.
The central architectural claim—that decoupling control state from the scheduler enables scalability and fault tolerance that no prior system achieved—is structurally difficult to validate through an ablation. You cannot "re-couple" the control state into the scheduler without building a different system entirely. The paper's strategy is to demonstrate the consequences of the design: linear scalability (Figure 8b), sub-30ms GCS failure recovery (Figure 10a), transparent task and actor reconstruction (Figures 11a, 11b), and scheduler-latency sensitivity of allreduce (Figure 12b). Each experiment validates a component of the claim, but the holistic claim rests on the architecture performing well across all experiments simultaneously—which it does.
The most compelling evidence is not any single experiment but the combination of the ES and PPO results with the microbenchmarks. The microbenchmarks show that the scheduler, GCS, and object store individually achieve the throughput and latency targets required for RL. The end-to-end applications show that these components working together can match or exceed specialized systems for real RL workloads. The gap between these two levels of evidence—the architectural principles and the application performance—is bridged by the paper's explanation of why the ES reference implementation fails at scale (centralized driver bottleneck) and why the MPI PPO implementation requires more GPUs (symmetric architecture, no heterogeneity awareness), which are precisely the limitations that Ray's architecture addresses.
6. Limitations and Trade-offs
The Achilles Heel: Difficulty Estimation Cost Is Not Accounted For in Headline Efficiency Numbers
The paper's compute-optimal scaling strategy depends entirely on the ability to estimate prompt difficulty before allocating the inference budget. The approach used—generating 2048 samples per question and averaging either ground-truth correctness (oracle) or PRM final-answer scores (predicted) to bin questions into five difficulty quintiles—is extraordinarily expensive. The paper acknowledges this explicitly in Section 3.2:
"estimating difficulty in this way still incurs additional computation cost during inference... our experiments do not account for this cost largely for simplicity"
The consequence is that the headline efficiency gains over best-of-N—which are the paper's most prominent quantitative claim—are computed after difficulty is known and do not amortize the cost of learning it. In a realistic deployment, the total computation would be difficulty estimation plus strategy execution. For a single question, generating 2048 samples for difficulty estimation alone consumes more compute than the largest test-time budgets studied (256–512 generations). Even if difficulty bins are estimated once per question type and amortized across many queries, the upfront cost is substantial. The figure should be understood as an upper bound on achievable efficiency under the assumption of zero-cost difficulty estimation—an assumption that does not hold in any deployment scenario described in the paper.
What evidence exists: Section 3.2 describes the estimation procedure and acknowledges the cost. The paper provides no experiment measuring end-to-end cost including difficulty estimation, no analysis of how many samples are actually needed for reliable binning, and no comparison showing whether coarser estimation (e.g., 16 or 64 samples instead of 2048) would preserve the compute-optimal gains. Figures 4 and 8 show compute-optimal curves rising from difficulty-estimated bins, but the x-axis (generation budget) excludes the estimation cost entirely. This is a significant gap between the paper's analytical framework and its practical deployability.
Mitigation status: The paper explicitly flags this as "a key avenue for future work" (Section 3.2) and suggests two directions—pretraining or fine-tuning models to directly predict difficulty from question text, and balancing the exploration-exploitation tradeoff (spending some inference compute to assess difficulty, then allocating the remainder). Neither direction is explored experimentally. Until this gap is closed, the compute-optimal framework is a compelling analysis tool but not a directly deployable system.
Hard Problems Remain Fundamentally Unsolved—Test-Time Compute Amplifies Capability but Does Not Create It
The paper's most sobering finding is that test-time compute provides essentially zero benefit on the hardest questions. Across all methods—search, revisions, and their compute-optimal combinations—difficulty bin 5 shows near-zero improvement regardless of compute budget. In Figure 3 (right), bin 5 accuracy hovers at 1–3% for all methods and all budgets from 4 to 256 generations. In Figure 7 (right), bin 5 shows roughly 2–3% accuracy irrespective of the sequential-to-parallel ratio at a budget of 128 generations. In the FLOPs-matched comparison (Figure 9), the bin 5 scaling line is essentially flat near 0–5%, sitting below the performance of the larger model across all values of .
The paper is candid about this boundary, stating in the Section 7 takeaway:
"test-time compute is most effective on problems where the base model already has some non-trivial probability of producing a correct answer... on problems outside the base model's capability range, additional test-time compute cannot compensate for the lack of pretraining"
This establishes a fundamental capability bound: test-time compute can amplify existing competence (finding and refining solutions the model already generates at some rate) but cannot create competence from nothing. If the base model's pass@1 on a problem class is near zero, no amount of search or revision—at any budget—will help, because there are no correct solutions in the proposal distribution to find or refine.
The consequence for practitioners is clear: if your problem distribution skews toward genuinely hard problems that the base model consistently fails on, investing in larger-scale pretraining is the only viable path forward. Test-time compute scaling is not a substitute for pretraining when capability gaps are fundamental. The paper's FLOPs-matched analysis quantifies where the crossover lies—hard problems favor pretraining across all regimes—but the boundary is defined by the base model's absolute capability, which is model-specific. A different base model with different strengths and weaknesses would have a different difficulty distribution and therefore different compute-optimal policies.
What evidence exists: Every difficulty-binned analysis in the paper (Figures 3 right, 7 right, 9) shows bin 5 as essentially flat and near-zero, regardless of method or budget. The FLOPs-matched comparison (Figure 9) explicitly shows the larger model outperforming test-time compute on bin 5 across all values. The paper does not explore whether a different base model—with different pretraining or architecture—would shift the difficulty distribution such that fewer problems fall into bin 5.
Mitigation status: The paper acknowledges the limitation explicitly (Section 7 takeaway, Section 8) but offers no mitigation within the test-time compute framework—it is a fundamental bound, not a fixable flaw. The only path forward for hard problems is improved pretraining, which is outside the scope of the paper's approach.
The Larger Model Baseline Is Weaker Than a Compute-Optimally Trained Model Would Be
The FLOPs-matched comparison in Section 7 scales model parameters while holding training data fixed, following the LLaMA paradigm (Touvron et al., 2023). The paper acknowledges this departs from compute-optimal pretraining where both data and parameters are scaled equally (Hoffmann et al., 2022):
"We choose this setting as it is representative of a canonical approach to scaling pretraining compute and leave the analysis of compute-optimal scaling of pretraining compute where the data and parameters are both scaled equally to future work."
The consequence is that the larger model used as the pretraining baseline may be under-trained relative to a Chinchilla-optimal model trained with the same total FLOPs. A compute-optimally trained larger model—scaling both parameters and data—would likely outperform a parameter-only-scaled model at the same FLOPs budget, making the pretraining baseline stronger than the one tested. This means the reported advantages of test-time compute over pretraining (e.g., +27.8% relative improvement on medium questions at for revisions, per Figure 1) may shrink or reverse against a properly compute-optimal larger model.
Additionally, the larger model uses only greedy decoding with no test-time compute augmentation of its own. The paper does not explore whether giving the larger model even a modest test-time compute budget (e.g., best-of-8 or majority voting) would shift the crossover point. This is an asymmetric comparison: the smaller model gets sophisticated test-time strategies while the larger model gets none. A fairer FLOPs-matched comparison would allocate some test-time compute to both models, proportional to their per-token costs.
What evidence exists: Section 7 describes the FLOP accounting and acknowledges the parameter-only scaling choice. The experimental results (Figure 9) are therefore specific to this particular pretraining baseline. The paper provides no comparison against a compute-optimally trained larger model, and the crossover points (where test-time compute wins vs. loses) should be interpreted as optimistic relative to what a Chinchilla-optimal pretraining baseline would show.
Mitigation status: The paper acknowledges the limitation and flags it for future work (Section 7, Section 8). No mitigation is attempted within the current experiments.
The Revision Model Suffers from a 38% Correct-to-Incorrect Reversion Rate and Fragile Training Dynamics
Section 6.1 reports a significant practical problem with the revision model: approximately 38% of correct answers produced during a revision chain get "revised" back to incorrect answers in the subsequent step. This is a direct consequence of the training data construction—the model sees only incorrect-to-correct trajectories during fine-tuning (0–4 incorrect answers followed by a correct answer), so it has no training signal for what to do when the current answer is already correct. When the model encounters a correct answer in its context during chain-of-revision, it defaults to its learned behavior of producing a different answer, which is often wrong.
The consequence is that sequential revisions are not monotonically improving—the chain can undo its own progress. The paper's mitigation is to use majority voting or verifier-based selection across the entire chain to pick the best answer from any point, rather than always taking the last revision. But this is a patch, not a solution: it means the system is generating and then discarding incorrect revisions that replaced correct answers, wasting compute. It also means that the pass@1 at each step (Figure 6, left) understates the model's true capability—the model might produce a correct answer at step 3, revise it to an incorrect answer at step 4, and then produce another correct answer at step 5, with the chain's value dependent entirely on the post-hoc selection mechanism.
The fragility of revision training is further underscored by the ReST experiment (Appendix K, Figure 16), where attempting to optimize the revision model with RL-style training caused performance to degrade substantially with sequential revisions. At 256 generations, fully sequential performance drops to approximately 33.5% compared to roughly 38.5% at the optimal ratio. The authors hypothesize that "on-policy data collection in ReST exacerbates spurious correlations in revision data, causing the model to fail to learn the revision task properly." This is a notable negative result that suggests the revision training procedure is sensitive to data collection methodology in ways that are not fully understood.
What evidence exists: The 38% reversion rate is stated in Section 6.1 (in the paragraph discussing the correct-to-incorrect reversion problem). The chain-of-revision pass@1 trajectory is shown in Figure 6 (left). The ReST failure is documented in Appendix K and Figure 16. Together, these results demonstrate that the revision approach works under specific training conditions (offline data construction, edit-distance-based pairing, base model fine-tuning) but degrades under others (on-policy data, RL optimization), and the mechanism for this sensitivity is not diagnosed.
Mitigation status: The paper acknowledges the correct-to-incorrect reversion problem and mitigates it partially through within-chain selection (majority or verifier). The ReST failure is presented as an observed limitation without a proposed fix. The paper does not explore training the revision model on trajectories that include correct answers with a "no revision needed" target, which would be a more principled solution to the reversion problem.
The Revision Model and PRM Search Are Studied Independently, Not Combined
The paper studies two complementary mechanisms—PRM-guided search (Section 5) and iterative revisions (Section 6)—as independent axes for improving test-time compute efficiency, but never combines them. Section 8 explicitly acknowledges this:
"we did not experiment with PRM tree-search techniques in combination with revisions"
This is a significant gap because the mechanisms have complementary, difficulty-dependent strengths: revisions improve the proposal distribution (generating better candidates through iterative refinement) and are most effective on easy problems, while PRM search improves candidate selection (finding the best among generated options) and is most effective on medium-hard problems. The paper's own analysis shows that the optimal strategy varies with difficulty—sequential revisions dominate on easy problems, beam search dominates on medium problems—which suggests that combining them (using a revision model as the proposal distribution within a PRM-guided beam search, or using the PRM to decide when to continue revising versus restart) could yield gains beyond either method alone.
The consequence is that the paper's results represent a lower bound on what a fully integrated system could achieve. The compute-optimal policy described in the paper selects between search and revisions per difficulty bin, but never uses both for the same problem. A combined approach might push the performance ceiling higher, particularly on medium-difficulty problems where both mechanisms show partial effectiveness. Without this experiment, the paper cannot claim that its compute-optimal allocation is truly optimal—it is optimal only within the restricted set of strategies tested, which excludes the natural combination of the two mechanisms.
What evidence exists: Section 5 evaluates search alone; Section 6 evaluates revisions alone; Section 8 acknowledges the combination was not tested. The difficulty-dependent behavior of each mechanism (search helps on medium, revisions help on easy) is documented in Figures 3 (right) and 7 (right), respectively, providing the motivation for combination. No experiment evaluates a hybrid approach.
Mitigation status: The paper flags this as future work in Section 8 but provides no preliminary results or detailed proposals for how the combination should work. The architecture of such a combined system—how the PRM would score revision steps, whether beam search would branch over revision alternatives, how the sequential-to-parallel ratio would interact with search beam width—is left entirely unspecified.
The Framework Is Validated on a Single Benchmark and Single Model Family
All experiments in the paper use the MATH benchmark (500 test questions) with PaLM 2-S* as the base model. The paper does not evaluate on other reasoning benchmarks (e.g., GSM8K for grade-school math, ARC for science reasoning, HumanEval for code generation) or with other model families (e.g., LLaMA, GPT, Mistral). The authors state they "believe this model is representative of the capabilities of many contemporary LLMs" (Section 4), but this claim is unverified.
The consequence is that the paper's key findings—the difficulty-dependent optimal strategy, the efficiency gains from compute-optimal allocation, the over-optimization behavior of beam search on easy problems, the superiority of last-step PRM aggregation, the FLOPs-matched crossover points—may be specific to the interaction between PaLM 2-S*'s output distribution and the MATH benchmark's mathematical reasoning structure. For instance, a model with different calibration properties might show different over-optimization thresholds for beam search. A model with stronger reasoning capabilities might push more problems into the "easy" bins, changing the optimal allocation distribution. A benchmark requiring code generation rather than symbolic math might favor different search strategies. The PRM training procedure—which uses Monte Carlo rollouts from the base model itself—would produce a different quality of verifier for a different base model, potentially shifting the over-optimization boundary.
The test set of 500 questions, split into five difficulty quintiles of roughly 100 each, then further split by two-fold cross-validation for strategy selection, means the compute-optimal policy is selected based on approximately 50 questions per fold per bin. This is a small sample, and the paper does not report confidence intervals on the compute-optimal scaling curves (Figures 4, 8), making it difficult to assess whether the observed gains are statistically reliable or sensitive to the specific folds. The ES and PPO experiments in the original paper show that Ray's scalability claims manifest differently depending on algorithmic details—similarly, the compute-optimal strategy findings could shift with a different model or benchmark.
What evidence exists: All experiments in Sections 5–7 use MATH with PaLM 2-S*. The difficulty estimation procedure (Section 3.2) uses 2048 samples per question from this specific base model. The PRM is trained on PaLM 2-S* rollouts and may not transfer to other models. No cross-model or cross-benchmark validation is presented.
Mitigation status: The paper does not directly address this limitation. The authors' belief that the model is "representative" is stated in Section 4 but not defended empirically. The paper suggests future work on extending to other domains (Section 8: "the same analysis could be applied to code generation, logical reasoning, and other structured prediction tasks") but does not provide preliminary evidence that the framework transfers.
7. Implications and Future Directions
How This Work Changes the Landscape
This paper fundamentally reframes how the systems community should think about the boundary between general-purpose distributed frameworks and specialized infrastructure for machine learning workloads. Before Ray, the dominant assumption—visible in the proliferation of one-off systems cited throughout the paper (AlphaGo's custom infrastructure, OpenAI's MPI-based PPO implementation, the Redis-based ES reference system)—was that the tight coupling of simulation, training, and serving in reinforcement learning required purpose-built distributed systems. Each new RL algorithm spawned its own communication protocols, its own fault tolerance mechanisms, its own scheduling logic. The paper's core empirical refutation is that this assumption, while understandable given the limitations of existing general-purpose frameworks, is architecturally contingent rather than inherent: it holds only when your general-purpose framework couples control state with scheduling and forces a single programming model (tasks-only or actors-only) on fundamentally heterogeneous workloads.
The magnitude of this shift is best understood as an architectural reframing that creates a new system category—the RL orchestration layer—rather than a paradigm shift in distributed computing theory. The paper does not introduce new scheduling algorithms with formal guarantees, new consistency protocols, or new fault tolerance mechanisms at the theoretical level. What it introduces is the recognition that the cross-cutting requirements of RL workloads (heterogeneous task durations, dynamic task graphs, stateful and stateless computation within the same application, unified fault tolerance across both modes) are not just a laundry list of features to add to existing systems, but rather a set of constraints that impose a specific architectural solution: decouple control state entirely from both scheduling and data transfer, keep every component except the metadata store stateless, and unify task-parallel and actor abstractions under a single lineage-based execution engine.
This reframing reconciles a tension that had been implicit in the distributed systems literature but never directly articulated. On one side, the dataflow community (Spark, CIEL, Dryad, Naiad) had demonstrated that lineage-based fault tolerance and dynamic task graphs could support increasingly complex workloads, but these systems remained confined to stateless computation and coarse-grained parallelism. On the other side, the actor community (Orleans, Akka, Erlang) had demonstrated that stateful, long-running computations could be made fault-tolerant and scalable, but these systems lacked the lightweight task abstraction that simulation workloads demand. The contradiction was that RL needed both, and the prevailing wisdom was that combining them would require either accepting the limitations of one paradigm (simulating state in a stateless framework, or simulating statelessness via short-lived actors) or stitching together multiple systems with prohibitive data movement costs.
Ray's resolution is that the conflict is not between statefulness and statelessness at the programming model level—both can coexist through a unified task graph with stateful edges—but rather between coupling and decoupling at the architectural level. A centralized scheduler that stores metadata cannot handle millions of fine-grained tasks per second; a decentralized scheduler that ignores data locality cannot efficiently support training workloads; an actor system without lineage-based reconstruction cannot provide transparent fault tolerance for stateless computation. The solution is not to pick one or compromise between them, but to recognize that control state must be a separate, scalable service that every other component queries without being bottlenecked by. The GCS is not an optimization—it is the architectural primitive that makes the unification possible.
The paper also shifts which research directions become more attractive, and which become less so:
-
More attractive: Research on general-purpose orchestration layers that coordinate specialized compute engines. The paper's demonstration that Ray can match Horovod on training throughput (Figure 13) and outperform OpenMPI on allreduce (Figure 12a) suggests that the performance cost of generality—long assumed to be prohibitive—can be driven low enough through architectural design that the benefits of a unified programming model outweigh the overhead. This opens space for orchestration frameworks in other domains (e.g., orchestrating data preprocessing, feature engineering, and model serving in supervised learning pipelines) that previously would have been dismissed as "too slow."
-
More attractive: Research on distributed schedulers that exploit the asymmetry between control-plane and data-plane operations. The bottom-up scheduler's key insight—that most tasks should be scheduled locally without touching a global coordinator, and that global scheduling is needed only for load balancing and resource heterogeneity—generalizes beyond RL. Any workload with producer-consumer locality (where tasks consume data produced by previous tasks on the same node) could benefit from this pattern.
-
More attractive: Research on lineage-based fault tolerance for hybrid stateless/stateful systems. The paper's stateful edge mechanism—embedding actor state transitions in an otherwise stateless task graph—is a specific solution to a specific problem, but the general principle (making implicit state dependencies explicit as graph edges to unify recovery) could apply to other systems that combine streaming state with batch computation.
-
Less attractive: Building one-off distributed systems for individual RL algorithms. The paper demonstrates that ES and PPO—two algorithms with very different communication patterns—can both be expressed efficiently in Ray's primitives. For new RL algorithms, the default question shifts from "what custom distributed infrastructure do I need?" to "can I express this in Ray, and if not, what specific primitive is missing?" The burden of proof shifts to the algorithm developer to justify custom infrastructure.
-
Less attractive: Research on making BSP systems more dynamic. The paper's simulation experiment (Table 4) shows a 1.8× throughput gap between Ray's asynchronous task execution and BSP-style execution, and this gap grows with scale. Efforts to patch dynamic execution into fundamentally synchronous frameworks face an uphill battle against architectures designed from the ground up for asynchrony.
Perhaps most significantly, the paper changes the economics of RL research. The authors report that porting the ES algorithm to Ray required modifying 7 lines of code from a serial implementation, while the reference implementation required "several hundred lines of code dedicated to a protocol for communicating tasks and data between workers" (Section 5.3.1). This is not just a convenience argument—it means that researchers who lack the resources to build custom distributed infrastructure can now evaluate their algorithms at scale. The paper notes that "several hundreds of people have used [Ray] and several companies are running it in production" within roughly a year of release (Section 7), suggesting that this accessibility argument resonated with practitioners. The downstream effect is that algorithmic innovation in RL can proceed faster because the systems engineering tax has been largely eliminated for algorithms that fit Ray's computation model.
Follow-Up Research This Work Enables
Garbage collection for distributed lineage in long-running RL applications. The paper identifies GCS flushing as a mechanism to bound lineage storage costs (Figure 10b), but the current implementation requires periodic disk snapshots and does not provide a general garbage collection policy. In long-running RL applications—where training may continue for days or weeks—the lineage graph grows without bound even with flushing, since flushing merely moves data to disk without deleting it. A follow-up study would design and evaluate a garbage collection policy that identifies when lineage records are no longer needed (e.g., when all consumers of an object have completed and the object itself has been evicted or checkpointed) and safely deletes them from the GCS. The key metric would be: for an RL workload running for 7 days (e.g., PPO on a complex continuous control task), can the GCS memory footprint be bounded at a fixed size (say, 10 GB) without impacting fault tolerance guarantees? The experiment would systematically vary garbage collection aggressiveness and measure (a) steady-state GCS memory, (b) recovery time after node failure as a function of how much lineage has been garbage collected, and (c) any false positives where lineage needed for reconstruction was prematurely deleted. This is newly tractable because the paper provides both the lineage logging infrastructure (GCS event logs) and a concrete workload (PPO on Humanoid-v1) where lineage growth rates can be measured.
Dynamic scheduling with partial task graph knowledge. The paper's scheduler makes greedy, per-task decisions without global knowledge of the computation graph (Section 7 acknowledges this as a limitation: "we must make scheduling decisions without full knowledge of the computation graph"). For RL workloads, the full graph is inherently unknowable in advance because future tasks depend on simulation results. However, many RL algorithms have predictable structural patterns: an ES workload alternates between broadcasting policies to thousands of workers (wide fan-out) and aggregating results (wide fan-in); a PPO workload alternates between collecting rollouts from a fixed pool of actors and updating a central policy. A strong follow-up would augment the global scheduler to accept optional hints about computation structure (e.g., "this task will spawn approximately N subtasks," or "this actor will receive gradients from M workers") and use these hints to proactively allocate resources or pre-replicate data. The evaluation would compare greedy scheduling against hint-augmented scheduling on ES and PPO workloads, measuring time-to-solution and resource utilization. The paper makes this tractable by providing baseline greedy scheduler performance (Figures 14a, 14b) and a scalable scheduler architecture where the global scheduler can be modified without changing the rest of the system.
Combining Ray's task and actor models with state-of-the-art collective communication libraries. The paper's allreduce implementation (Figure 12a) outperforms OpenMPI on large objects but underperforms on small objects (10MB) because OpenMPI switches to a lower-overhead algorithm. Since the paper's publication, collective communication libraries like NCCL (for GPU-aware allreduce) and optimized MPI implementations have continued to improve. A follow-up study would benchmark Ray's native allreduce (implemented via actors and the object store) against the latest NCCL version across a range of object sizes, GPU topologies, and node counts for realistic training workloads (e.g., GPT-2 scale models, not just ResNet-101). The specific question: does Ray's architectural overhead (GCS queries, object store replication) become a bottleneck for modern training at scale, or does the pipelining optimization described in Section 5.2.1 (overlapping gradient computation with transfer) hide this overhead entirely? The paper's training benchmark (Figure 13) shows Ray within 10% of distributed TensorFlow at 64 GPUs, but this was measured on ResNet-101 with synthetic data in 2018. Re-measuring on modern hardware with larger models would determine whether the decoupled control plane imposes a fundamental scaling tax that grows with model size.
Spot-instance fault tolerance for end-to-end RL training with realistic preemption patterns. The paper argues that fault tolerance enables spot instance usage and claims an 18× cost reduction for PPO (Section 5.3.2), but this number multiplies independently estimated factors (4.5× from heterogeneity-aware scheduling, 4× from spot vs. on-demand pricing) without demonstrating actual spot-instance RL training completing successfully. A strong follow-up would run a 24-hour PPO training job on spot instances with realistic preemption patterns (using AWS spot instance preemption data or a synthetic preemption model calibrated to historical rates) and measure: (a) does the job complete successfully and reach the target score? (b) what is the total wall-clock time including reconstruction delays? (c) how does the time-to-solution compare to on-demand instances, factoring in preemption overhead? The paper's actor reconstruction experiment (Figure 11b) shows that 400 actors can be recovered in ~70 seconds, but this was a controlled failure of 2 out of 10 nodes—realistic spot preemption might involve simultaneous termination of many nodes, cascading reconstruction across actors and tasks simultaneously, and repeated preemptions during recovery. This stress test would validate or refute the paper's economic argument in a realistic setting.
Difficulty prediction models trained directly from question text to replace the 2048-sample estimation procedure. The paper's most significant practical limitation is the cost of difficulty estimation (2048 samples per question), which the authors flag as "a key avenue for future work" (Section 3.2). A direct follow-up would train a lightweight classifier—a small transformer or even a linear model on top of the base model's embeddings—that takes only the question text as input and predicts the difficulty quintile. The training data already exists: the paper has oracle difficulty labels (from 2048-sample pass@1 estimation) for all 12,000 training questions and 500 test questions in the MATH dataset. The evaluation would compare compute-optimal strategy performance using (a) the expensive PRM-based difficulty estimation (current method), (b) the lightweight classifier, and (c) a simple heuristic like question length or average token probability from the base model. If the classifier achieves comparable accuracy to the PRM-based method—even if it's slightly worse on individual questions—the aggregate compute-optimal scaling curves might be nearly identical, as the paper showed that oracle and predicted difficulty bins produce largely overlapping curves (Figures 4, 8). This would close the gap between the paper's analytical framework and practical deployability.
Combined PRM search and revision model to test whether the two mechanisms are additive or redundant. The paper studies PRM search (Section 5) and iterative revisions (Section 6) independently, and Section 8 acknowledges they were never combined. The most natural follow-up is to use the revision model as the proposal distribution within a PRM-guided search: at each step of beam search, the model conditions on the partial solution history (including rejected branches) to produce the next step, rather than generating each beam independently from the base model. A concrete experiment would compare three conditions on the MATH benchmark: (a) PRM beam search alone (the best configuration from Section 5), (b) sequential revisions alone (the best configuration from Section 6), and (c) the combined system where beam search over revision model outputs uses the PRM to score steps and prune beams. The key question is whether the mechanisms are additive (combined accuracy ≈ sum of individual improvements), redundant (combined accuracy ≈ max of individual), or synergistic (combined accuracy > sum). The paper's difficulty-dependent analysis (search helps on medium, revisions help on easy) suggests that the answer may vary by difficulty bin—the combined system might outperform either alone on medium problems where both exploration (search) and refinement (revisions) matter, while adding little on easy problems where revisions already suffice. This experiment would directly test the paper's core thesis that test-time compute allocation should be adaptive and multi-modal.
Practical Applications and Downstream Use Cases
On-demand simulation clusters for RL research teams. Research groups that develop new RL algorithms typically need to run evaluation sweeps across hyperparameters, random seeds, and environment variants. Before Ray, each researcher either used a single machine (limiting scale) or built custom distributed infrastructure (consuming engineering time). With Ray, a team can allocate a heterogeneous cluster—GPU instances for policy training, CPU instances for simulation—and run multiple concurrent RL experiments with transparent fault tolerance. The paper's PPO results quantify the benefit: the Ray implementation runs on arbitrary mixes of GPU and CPU instances, automatically scheduling GPU-requiring training tasks on GPU nodes and CPU-only simulation tasks on cheaper CPU nodes, yielding a 4.5× cost reduction compared to homogeneous MPI deployments (Section 5.3.2). For a research group spending 11,000/month for equivalent throughput, or the ability to run 4.5× more experiments within the same budget. The fault tolerance further allows the use of spot instances (approximately 4× cheaper than on-demand), potentially reducing costs to roughly $2,800/month—an 18× total reduction per the paper's estimate.
Production RL pipelines with tight serving-simulation-training loops. Companies deploying RL in production—for recommendation systems, automated trading, robotics control, or game AI—face the architectural challenge the paper describes: simulation (or real-environment interaction) produces data that must immediately feed into training, which must immediately update the policy being served. Stitching together separate systems (e.g., a stream processor for data ingestion, a training cluster for model updates, a model server for policy serving) introduces latency at each boundary that slows the learning loop. Ray provides a single framework where a driver process orchestrates the entire loop: simulation actors generate trajectories, a training actor updates the policy, and the updated policy is served to the same simulation actors via shared memory—all within a single application with unified fault tolerance. The embedded serving results (Table 3) show that Ray serves policies to co-located simulators at 6,200–6,900 states/second, an order of magnitude faster than going through an external serving system like Clipper for large-input models. For a production application requiring 10,000 policy evaluations per second (e.g., a recommendation system serving 10,000 users simultaneously), this performance eliminates the need for a separate model-serving cluster and the associated operational complexity.
Cost-efficient hyperparameter optimization for deep learning. While the paper focuses on RL, the underlying architecture—a general-purpose distributed task execution engine with heterogeneous resource scheduling—applies directly to hyperparameter optimization (HPO) workloads. A typical HPO run (e.g., using Bayesian optimization or population-based training) spawns hundreds of training trials with different hyperparameters, each potentially requiring different resources (some trials might need GPUs for large models, others only CPUs for smaller models). Running this on a homogeneous cluster forces all nodes to have GPUs, even when many trials don't need them. Ray's resource specification API (@ray.remote(num_gpus=2)) allows each trial to declare its requirements, and the bottom-up scheduler places GPU-requiring trials on GPU nodes while filling CPU nodes with non-GPU trials. The training throughput results (Figure 13) demonstrate that Ray's overhead for distributed training is within 10% of native distributed TensorFlow, meaning the HPO workflow would lose minimal efficiency per trial while gaining the ability to use heterogeneous hardware. For an HPO sweep involving 1000 trials on a mix of ResNet-50 (GPU) and logistic regression (CPU-only) models, this could reduce total cluster cost by packing CPU-only work onto cheaper instances rather than idling expensive GPU nodes.
Distributed data processing with embedded model inference. The paper's actor model provides a natural way to embed trained models within data processing pipelines. Consider a video processing workflow where each frame must be passed through an object detector (GPU-accelerated) and the resulting bounding boxes are used to crop regions that are then classified by a smaller model (CPU). In a traditional architecture, this would involve separate serving infrastructure for each model, with serialization and network overhead between stages. In Ray, the detector model is loaded into a GPU actor, the classifier into CPU actors, and the workflow is expressed as a pipeline of tasks that route data between them, with intermediate results stored in the shared-memory object store. The serving results (Table 3) show that Ray's embedded serving achieves 6,900 states/second for a large-input model versus 290 states/second through an external serving system (Clipper via REST), an order-of-magnitude throughput advantage that comes from eliminating serialization and network overhead between pipeline stages. For a video pipeline processing 30 frames per second at 1080p resolution, this throughput gap determines whether the pipeline can run in real-time or requires batch processing.
When to Prefer Ray Over Alternative Architectures
The paper articulates a clear tradeoff between Ray and three categories of alternatives, grounded in the architectural limitations of each. The decision rules are:
Prefer Ray over BSP frameworks (Spark, MapReduce) when:
- Tasks within a single application have heterogeneous durations (milliseconds to hours), because BSP barriers force all tasks to wait for the slowest member of each stage. The paper quantifies this: Ray achieves 1.8× higher simulation throughput than BSP-MPI on 256 cores (Table 4) because completed simulations can be processed immediately rather than waiting at a barrier.
- The computation graph is dynamic and data-dependent—future tasks depend on the results of current tasks and cannot be enumerated in advance. The paper's ES and PPO applications demonstrate this pattern: the number of rollouts per training iteration and the decision to terminate exploration early both depend on intermediate results.
- Fine-grained tasks (millisecond durations) dominate the workload, because BSP systems amortize scheduling overhead over coarse-grained stages and become inefficient when per-task overhead approaches task duration.
Prefer Ray over actor-only frameworks (Orleans, Akka) when:
- A significant fraction of the workload is stateless and embarrassingly parallel (e.g., simulation rollouts), because tasks provide data locality and fine-grained load balancing that actors cannot match. Figure 8a shows that tasks placed with locality awareness maintain constant latency regardless of input size, while locality-unaware placement (as required by fixed actor locations) incurs 1-2 orders of magnitude latency penalty for 10-100MB inputs.
- Transparent recovery of stateless computation is needed—actors in Orleans and Akka require explicit developer checkpointing, while Ray's lineage-based reconstruction recovers lost stateless tasks automatically without developer effort (Figure 11a).
Prefer Ray over MPI-based systems (OpenMPI, custom MPI implementations) when:
- Resources are heterogeneous within a single application—some tasks require GPUs, others only CPUs. The paper's PPO results (Figure 14b) show that MPI forces symmetric architectures where all processes require identical resources, while Ray's resource specification API allows CPU-only tasks to run on cheaper instances, reducing cost by 4.5×.
- Transparent fault tolerance is valued—MPI jobs typically fail entirely when any node fails, requiring manual restart. Ray's lineage-based reconstruction (Figures 11a, 11b) enables the use of spot instances with automatic recovery, which the paper estimates yields an additional 4× cost reduction.
- The computation involves dynamic, data-dependent task creation (nested parallelism). MPI's process model makes dynamic task spawning complex and error-prone; Ray's nested remote functions express this pattern naturally and the bottom-up scheduler handles the resulting dynamic task graph.
Prefer Ray over specialized RL systems (custom infrastructure) when:
- Developing a new algorithm or extending an existing one, because the engineering cost of building custom distributed infrastructure—"several hundred lines of code dedicated to a protocol for communicating tasks and data between workers" (Section 5.3.1)—is avoided. The paper shows that ES parallelization required 7 lines of Ray code.
- The algorithm's computation pattern fits within Ray's task-actor model—iterative scatter-gather, parameter-server training, or asynchronous rollouts with centralized policy updates. For algorithms with fundamentally different communication patterns (e.g., decentralized multi-agent RL with peer-to-peer communication between agents), the paper does not provide evidence that Ray's primitives are sufficient, and specialized systems may still be necessary.
The boundary where Ray is less suitable: The paper explicitly acknowledges that Ray is "not a substitute for generic data-parallel frameworks, such as Spark, as it currently lacks the rich functionality and APIs (e.g., straggler mitigation, query optimization) that these frameworks provide" (Section 1). For workloads that map cleanly onto SQL queries, relational operations, or bulk ETL, Spark's optimizer and dataframe API provide productivity benefits that Ray's lower-level task/actor model does not. Similarly, for serving external clients with requirements for authentication, rate limiting, model versioning, and A/B testing, "Ray does not aim to substitute for serving systems like Clipper and TensorFlow Serving" (Section 1). The paper's embedded serving advantage (Table 3) applies specifically to co-located serving within a Ray application, not to the general model serving problem.