What is Ray?

https://github.com/ray-project/ray/raw/master/doc/source/images/ray_header_logo.png

Ray provides a simple, universal API for building distributed applications.

Ray accomplishes this mission by:

  1. Providing simple primitives for building and running distributed applications.

  2. Enabling end users to parallelize single machine code, with little to zero code changes.

  3. Including a large ecosystem of applications, libraries, and tools on top of the core Ray to enable complex applications.

Ray Core provides the simple primitives for application building.

On top of Ray Core are several libraries for solving problems in machine learning:

There are also many community integrations with Ray, including Dask, MARS, Modin, Horovod, Hugging Face, Scikit-learn, and others. Check out the full list of Ray distributed libraries here.

Getting Started with Ray

Check out A Gentle Introduction to Ray to learn more about Ray and its ecosystem of libraries that enable things like distributed hyperparameter tuning, reinforcement learning, and distributed training.

Ray provides Python, Java, and EXPERIMENTAL C++ API. And Ray uses Tasks (functions) and Actors (Classes) to allow you to parallelize your code.

# First, run `pip install ray`.

import ray
ray.init()

@ray.remote
def f(x):
    return x * x

futures = [f.remote(i) for i in range(4)]
print(ray.get(futures)) # [0, 1, 4, 9]

@ray.remote
class Counter(object):
    def __init__(self):
        self.n = 0

    def increment(self):
        self.n += 1

    def read(self):
        return self.n

counters = [Counter.remote() for i in range(4)]
[c.increment.remote() for c in counters]
futures = [c.read.remote() for c in counters]
print(ray.get(futures)) # [1, 1, 1, 1]

First, add the ray-api and ray-runtime dependencies in your project.

import io.ray.api.ActorHandle;
import io.ray.api.ObjectRef;
import io.ray.api.Ray;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

public class RayDemo {

  public static int square(int x) {
    return x * x;
  }

  public static class Counter {

    private int value = 0;

    public void increment() {
      this.value += 1;
    }

    public int read() {
      return this.value;
    }
  }

  public static void main(String[] args) {
    // Intialize Ray runtime.
    Ray.init();
    {
      List<ObjectRef<Integer>> objectRefList = new ArrayList<>();
      // Invoke the `square` method 4 times remotely as Ray tasks.
      // The tasks will run in parallel in the background.
      for (int i = 0; i < 4; i++) {
        objectRefList.add(Ray.task(RayDemo::square, i).remote());
      }
      // Get the actual results of the tasks with `get`.
      System.out.println(Ray.get(objectRefList));  // [0, 1, 4, 9]
    }

    {
      List<ActorHandle<Counter>> counters = new ArrayList<>();
      // Create 4 actors from the `Counter` class.
      // They will run in remote worker processes.
      for (int i = 0; i < 4; i++) {
        counters.add(Ray.actor(Counter::new).remote());
      }

      // Invoke the `increment` method on each actor.
      // This will send an actor task to each remote actor.
      for (ActorHandle<Counter> counter : counters) {
        counter.task(Counter::increment).remote();
      }
      // Invoke the `read` method on each actor, and print the results.
      List<ObjectRef<Integer>> objectRefList = counters.stream()
          .map(counter -> counter.task(Counter::read).remote())
          .collect(Collectors.toList());
      System.out.println(Ray.get(objectRefList));  // [1, 1, 1, 1]
    }
  }
}
The C++ Ray API is currently experimental with limited support. You can track its development here and report issues on GitHub.
Run the following commands to get started:
- Build ray from source with bazel as shown here.
- Run “cd ray/cpp”.
- Run “cp dev_BUILD.bazel BUILD.bazel”.
- Modify src/ray/example/example.cc.
- Run “ray stop”.
- Run “bazel build //cpp:all”.
- Run “bazel run //cpp:example”.
#include <ray/api.h>
#include <ray/api/ray_config.h>
#include <ray/experimental/default_worker.h>

using namespace ::ray::api;

/// general function of user code
int Return1() { return 1; }
int Plus1(int x) { return x + 1; }
int Plus(int x, int y) { return x + y; }

/// a class of user code
class Counter {
 public:
  int count;

  Counter(int init) { count = init; }
  static Counter *FactoryCreate() { return new Counter(0); }
  static Counter *FactoryCreate(int init) { return new Counter(init); }
  static Counter *FactoryCreate(int init1, int init2) {
    return new Counter(init1 + init2);
  }
  /// non static function
  int Plus1() {
    count += 1;
    return count;
  }
  int Add(int x) {
    count += x;
    return count;
  }
};

int main(int argc, char **argv) {
  /// Currently, we compile `default_worker` and `example` in one single binary,
  /// to work around a symbol conflicting issue.
  /// This is the main function of the binary, and we use the `is_default_worker` arg to
  /// tell if this binary is used as `default_worker` or `example`.
  const char *default_worker_magic = "is_default_worker";
  /// `is_default_worker` is the last arg of `argv`
  if (argc > 1 &&
      memcmp(argv[argc - 1], default_worker_magic, strlen(default_worker_magic)) == 0) {
    default_worker_main(argc, argv);
    return 0;
  }
  /// initialization to cluster mode
  ray::api::RayConfig::GetInstance()->run_mode = RunMode::CLUSTER;
  /// Dynamic library loading is not supported yet.
  ray::api::RayConfig::GetInstance()->lib_name = "";
  Ray::Init();

  /// put and get object
  auto obj = Ray::Put(12345);
  auto get_put_result = *(Ray::Get(obj));
  std::cout << "get_put_result = " << get_put_result << std::endl;

  /// common task without args
  auto task_obj = Ray::Task(Return1).Remote();
  int task_result1 = *(Ray::Get(task_obj));
  std::cout << "task_result1 = " << task_result1 << std::endl;

  /// common task with args
  task_obj = Ray::Task(Plus1, 5).Remote();
  int task_result2 = *(Ray::Get(task_obj));
  std::cout << "task_result2 = " << task_result2 << std::endl;

  /// actor task without args
  ActorHandle<Counter> actor1 = Ray::Actor(Counter::FactoryCreate).Remote();
  auto actor_object1 = actor1.Task(&Counter::Plus1).Remote();
  int actor_result1 = *(Ray::Get(actor_object1));
  std::cout << "actor_result1 = " << actor_result1 << std::endl;

  /// actor task with args
  ActorHandle<Counter> actor2 = Ray::Actor(Counter::FactoryCreate, 1).Remote();
  auto actor_object2 = actor2.Task(&Counter::Add, 5).Remote();
  int actor_result2 = *(Ray::Get(actor_object2));
  std::cout << "actor_result2 = " << actor_result2 << std::endl;

  /// actor task with args which pass by reference
  ActorHandle<Counter> actor3 = Ray::Actor(Counter::FactoryCreate, 6, 0).Remote();
  auto actor_object3 = actor3.Task(&Counter::Add, actor_object2).Remote();
  int actor_result3 = *(Ray::Get(actor_object3));
  std::cout << "actor_result3 = " << actor_result3 << std::endl;

  /// general function remote call(args passed by value)
  auto r0 = Ray::Task(Return1).Remote();
  auto r2 = Ray::Task(Plus, 3, 22).Remote();

  int task_result3 = *(Ray::Get(r2));
  std::cout << "task_result3 = " << task_result3 << std::endl;

  /// general function remote call(args passed by reference)
  auto r3 = Ray::Task(Return1).Remote();
  auto r4 = Ray::Task(Plus1, r3).Remote();
  auto r5 = Ray::Task(Plus, r4, r3).Remote();
  auto r6 = Ray::Task(Plus, r4, 10).Remote();

  int task_result4 = *(Ray::Get(r6));
  int task_result5 = *(Ray::Get(r5));
  std::cout << "task_result4 = " << task_result4 << ", task_result5 = " << task_result5
            << std::endl;

  /// create actor and actor function remote call with args passed by value
  ActorHandle<Counter> actor4 = Ray::Actor(Counter::FactoryCreate, 10).Remote();
  auto r10 = actor4.Task(&Counter::Add, 8).Remote();

  int actor_result4 = *(Ray::Get(r10));
  std::cout << "actor_result4 = " << actor_result4 << std::endl;

  /// create actor and task function remote call with args passed by reference
  ActorHandle<Counter> actor5 = Ray::Actor(Counter::FactoryCreate, r10, 0).Remote();

  auto r11 = actor5.Task(&Counter::Add, r0).Remote();
  auto r12 = actor5.Task(&Counter::Add, r11).Remote();
  auto r13 = actor5.Task(&Counter::Add, r10).Remote();
  auto r14 = actor5.Task(&Counter::Add, r13).Remote();
  auto r15 = Ray::Task(Plus, r0, r11).Remote();
  auto r16 = Ray::Task(Plus1, r15).Remote();

  int result12 = *(Ray::Get(r12));
  int result14 = *(Ray::Get(r14));
  int result11 = *(Ray::Get(r11));
  int result13 = *(Ray::Get(r13));
  int result16 = *(Ray::Get(r16));
  int result15 = *(Ray::Get(r15));

  std::cout << "Final result:" << std::endl;
  std::cout << "result11 = " << result11 << ", result12 = " << result12
            << ", result13 = " << result13 << ", result14 = " << result14
            << ", result15 = " << result15 << ", result16 = " << result16 << std::endl;
  Ray::Shutdown();
  return 0;
}

You can also get started by visiting our Tutorials. For the latest wheels (nightlies), see the installation page.

Getting Involved

Ray is more than a framework for distributed applications but also an active community of developers, researchers, and folks that love machine learning. Here’s a list of tips for getting involved with the Ray community:

If you’re interested in contributing to Ray, visit our page on Getting Involved to read about the contribution process and see what you can work on!

More Information

Here are some talks, papers, and press coverage involving Ray and its libraries. Please raise an issue if any of the below links are broken, or if you’d like to add your own talk!