Back to Ray

Cross-language programming

doc/source/ray-core/cross-language.rst

1.13.18.4 KB
Original Source

.. _cross_language:

Cross-language programming

This page shows you how to use Ray's cross-language programming feature.

Setup the driver

You need to set :ref:code_search_path in your driver.

.. tab-set::

.. tab-item:: Python

    .. literalinclude:: ./doc_code/cross_language.py
        :language: python
        :start-after: __crosslang_init_start__
        :end-before: __crosslang_init_end__

.. tab-item:: Java

    .. code-block:: bash

        java -classpath <classpath> \
            -Dray.address=<address> \
            -Dray.job.code-search-path=/path/to/code/ \
            <classname> <args>

You may want to include multiple directories to load both Python and Java code for workers, if you place them in different directories.

.. tab-set::

.. tab-item:: Python

    .. literalinclude:: ./doc_code/cross_language.py
        :language: python
        :start-after: __crosslang_multidir_start__
        :end-before: __crosslang_multidir_end__

.. tab-item:: Java

    .. code-block:: bash

        java -classpath <classpath> \
            -Dray.address=<address> \
            -Dray.job.code-search-path=/path/to/jars:/path/to/pys \
            <classname> <args>

Python calling Java

Suppose you have a Java static method and a Java class as follows:

.. code-block:: java

package io.ray.demo;

public class Math {

public static int add(int a, int b) {
  return a + b;
}

}

.. code-block:: java

package io.ray.demo;

// A regular Java class. public class Counter {

private int value = 0;

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

}

Then, in Python, you can call the preceding Java remote function, or create an actor from the preceding Java class.

.. literalinclude:: ./doc_code/cross_language.py :language: python :start-after: python_call_java_start :end-before: python_call_java_end

Java calling Python

Suppose you have a Python module as follows:

.. literalinclude:: ./doc_code/cross_language.py :language: python :start-after: python_module_start :end-before: python_module_end

.. note::

  • You should decorate the function or class with @ray.remote.

Then, in Java, you can call the preceding Python remote function, or create an actor from the preceding Python class.

.. code-block:: java

package io.ray.demo;

import io.ray.api.ObjectRef; import io.ray.api.PyActorHandle; import io.ray.api.Ray; import io.ray.api.function.PyActorClass; import io.ray.api.function.PyActorMethod; import io.ray.api.function.PyFunction; import org.testng.Assert;

public class JavaCallPythonDemo {

public static void main(String[] args) {
  // Set the code-search-path to the directory of your `ray_demo.py` file.
  System.setProperty("ray.job.code-search-path", "/path/to/the_dir/");
  Ray.init();

  // Define a Python class.
  PyActorClass actorClass = PyActorClass.of(
      "ray_demo", "Counter");

  // Create a Python actor and call actor method.
  PyActorHandle actor = Ray.actor(actorClass).remote();
  ObjectRef objRef1 = actor.task(
      PyActorMethod.of("increment", int.class)).remote();
  Assert.assertEquals(objRef1.get(), 1);
  ObjectRef objRef2 = actor.task(
      PyActorMethod.of("increment", int.class)).remote();
  Assert.assertEquals(objRef2.get(), 2);

  // Call the Python remote function.
  ObjectRef objRef3 = Ray.task(PyFunction.of(
      "ray_demo", "add", int.class), 1, 2).remote();
  Assert.assertEquals(objRef3.get(), 3);

  Ray.shutdown();
}

}

Cross-language data serialization

Ray automatically serializes and deserializes the arguments and return values of Ray calls if their types are the following:

  • Primitive data types =========== ======= ======= MessagePack Python Java =========== ======= ======= nil None null bool bool Boolean int int Short / Integer / Long / BigInteger float float Float / Double str str String bin bytes byte[] =========== ======= =======

  • Basic container types =========== ======= ======= MessagePack Python Java =========== ======= ======= array list Array =========== ======= =======

  • Ray builtin types

    • ActorHandle

.. note::

  • Be aware of float / double precision between Python and Java. If Java is using a float type to receive the input argument, the double precision Python data reduces to float precision in Java.
  • BigInteger can support a max value of 2^64-1. See: https://github.com/msgpack/msgpack/blob/master/spec.md#int-format-family. If the value is larger than 2^64-1, then sending the value to Python raises an exception.

The following example shows how to pass these types as parameters and how to return these types.

You can write a Python function which returns the input data:

.. literalinclude:: ./doc_code/cross_language.py :language: python :start-after: serialization_start :end-before: serialization_end

Then you can transfer the object from Java to Python, and back from Python to Java:

.. code-block:: java

package io.ray.demo;

import io.ray.api.ObjectRef; import io.ray.api.Ray; import io.ray.api.function.PyFunction; import java.math.BigInteger; import org.testng.Assert;

public class SerializationDemo {

public static void main(String[] args) {
  Ray.init();

  Object[] inputs = new Object[]{
      true,  // Boolean
      Byte.MAX_VALUE,  // Byte
      Short.MAX_VALUE,  // Short
      Integer.MAX_VALUE,  // Integer
      Long.MAX_VALUE,  // Long
      BigInteger.valueOf(Long.MAX_VALUE),  // BigInteger
      "Hello World!",  // String
      1.234f,  // Float
      1.234,  // Double
      "example binary".getBytes()};  // byte[]
  for (Object o : inputs) {
    ObjectRef res = Ray.task(
        PyFunction.of("ray_serialization", "py_return_input", o.getClass()),
        o).remote();
    Assert.assertEquals(res.get(), o);
  }

  Ray.shutdown();
}

}

Cross-language exception stacks

Suppose you have a Java package as follows:

.. code-block:: java

package io.ray.demo;

import io.ray.api.ObjectRef; import io.ray.api.Ray; import io.ray.api.function.PyFunction;

public class MyRayClass {

public static int raiseExceptionFromPython() {
  PyFunction<Integer> raiseException = PyFunction.of(
      "ray_exception", "raise_exception", Integer.class);
  ObjectRef<Integer> refObj = Ray.task(raiseException).remote();
  return refObj.get();
}

}

and a Python module as follows:

.. literalinclude:: ./doc_code/cross_language.py :language: python :start-after: raise_exception_start :end-before: raise_exception_end

Then, run the following code:

.. literalinclude:: ./doc_code/cross_language.py :language: python :start-after: raise_exception_demo_start :end-before: raise_exception_demo_end

The exception stack will be:

.. code-block:: text

Traceback (most recent call last): File "ray_exception_demo.py", line 9, in <module> ray.get(obj_ref) # <-- raise exception from here. File "ray/python/ray/_private/client_mode_hook.py", line 105, in wrapper return func(*args, **kwargs) File "ray/python/ray/_private/worker.py", line 2247, in get raise value ray.exceptions.CrossLanguageError: An exception raised from JAVA: io.ray.api.exception.RayTaskException: (pid=61894, ip=172.17.0.2) Error executing task c8ef45ccd0112571ffffffffffffffffffffffff01000000 at io.ray.runtime.task.TaskExecutor.execute(TaskExecutor.java:186) at io.ray.runtime.RayNativeRuntime.nativeRunTaskExecutor(Native Method) at io.ray.runtime.RayNativeRuntime.run(RayNativeRuntime.java:231) at io.ray.runtime.runner.worker.DefaultWorker.main(DefaultWorker.java:15) Caused by: io.ray.api.exception.CrossLanguageException: An exception raised from PYTHON: ray.exceptions.RayTaskError: ray::raise_exception() (pid=62041, ip=172.17.0.2) File "ray_exception.py", line 7, in raise_exception 1 / 0 ZeroDivisionError: division by zero