cross-language.rst 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344
  1. .. _cross_language:
  2. Cross-language programming
  3. ==========================
  4. This page will show you how to use Ray's cross-language programming feature.
  5. Setup the driver
  6. -----------------
  7. We need to set :ref:`code_search_path` in your driver.
  8. .. tabs::
  9. .. group-tab:: Python
  10. .. code-block:: python
  11. ray.init(job_config=ray.job_config.JobConfig(code_search_path="/path/to/code"))
  12. .. group-tab:: Java
  13. .. code-block:: bash
  14. java -classpath <classpath> \
  15. -Dray.address=<address> \
  16. -Dray.job.code-search-path=/path/to/code/ \
  17. <classname> <args>
  18. You may want to include multiple directories to load both Python and Java code for workers, if they are placed in different directories.
  19. .. tabs::
  20. .. group-tab:: Python
  21. .. code-block:: python
  22. ray.init(job_config=ray.job_config.JobConfig(code_search_path="/path/to/jars:/path/to/pys"))
  23. .. group-tab:: Java
  24. .. code-block:: bash
  25. java -classpath <classpath> \
  26. -Dray.address=<address> \
  27. -Dray.job.code-search-path=/path/to/jars:/path/to/pys \
  28. <classname> <args>
  29. Python calling Java
  30. -------------------
  31. Suppose we have a Java static method and a Java class as follows:
  32. .. code-block:: java
  33. package io.ray.demo;
  34. public class Math {
  35. public static int add(int a, int b) {
  36. return a + b;
  37. }
  38. }
  39. .. code-block:: java
  40. package io.ray.demo;
  41. // A regular Java class.
  42. public class Counter {
  43. private int value = 0;
  44. public int increment() {
  45. this.value += 1;
  46. return this.value;
  47. }
  48. }
  49. Then, in Python, we can call the above Java remote function, or create an actor
  50. from the above Java class.
  51. .. code-block:: python
  52. import ray
  53. ray.init(address="auto")
  54. # Define a Java class.
  55. counter_class = ray.java_actor_class(
  56. "io.ray.demo.Counter")
  57. # Create a Java actor and call actor method.
  58. counter = counter_class.remote()
  59. obj_ref1 = counter.increment.remote()
  60. assert ray.get(obj_ref1) == 1
  61. obj_ref2 = counter.increment.remote()
  62. assert ray.get(obj_ref2) == 2
  63. # Define a Java function.
  64. add_function = ray.java_function(
  65. "io.ray.demo.Math", "add")
  66. # Call the Java remote function.
  67. obj_ref3 = add_function.remote(1, 2)
  68. assert ray.get(obj_ref3) == 3
  69. ray.shutdown()
  70. Java calling Python
  71. -------------------
  72. Suppose we have a Python module as follows:
  73. .. code-block:: python
  74. # ray_demo.py
  75. import ray
  76. @ray.remote
  77. class Counter(object):
  78. def __init__(self):
  79. self.value = 0
  80. def increment(self):
  81. self.value += 1
  82. return self.value
  83. @ray.remote
  84. def add(a, b):
  85. return a + b
  86. .. note::
  87. * The function or class should be decorated by `@ray.remote`.
  88. Then, in Java, we can call the above Python remote function, or create an actor
  89. from the above Python class.
  90. .. code-block:: java
  91. package io.ray.demo;
  92. import io.ray.api.ObjectRef;
  93. import io.ray.api.PyActorHandle;
  94. import io.ray.api.Ray;
  95. import io.ray.api.function.PyActorClass;
  96. import io.ray.api.function.PyActorMethod;
  97. import io.ray.api.function.PyFunction;
  98. import org.testng.Assert;
  99. public class JavaCallPythonDemo {
  100. public static void main(String[] args) {
  101. Ray.init();
  102. // Define a Python class.
  103. PyActorClass actorClass = PyActorClass.of(
  104. "ray_demo", "Counter");
  105. // Create a Python actor and call actor method.
  106. PyActorHandle actor = Ray.actor(actorClass).remote();
  107. ObjectRef objRef1 = actor.task(
  108. PyActorMethod.of("increment", int.class)).remote();
  109. Assert.assertEquals(objRef1.get(), 1);
  110. ObjectRef objRef2 = actor.task(
  111. PyActorMethod.of("increment", int.class)).remote();
  112. Assert.assertEquals(objRef2.get(), 2);
  113. // Call the Python remote function.
  114. ObjectRef objRef3 = Ray.task(PyFunction.of(
  115. "ray_demo", "add", int.class), 1, 2).remote();
  116. Assert.assertEquals(objRef3.get(), 3);
  117. Ray.shutdown();
  118. }
  119. }
  120. Cross-language data serialization
  121. ---------------------------------
  122. The arguments and return values of ray call can be serialized & deserialized
  123. automatically if their types are the following:
  124. - Primitive data types
  125. =========== ======= =======
  126. MessagePack Python Java
  127. =========== ======= =======
  128. nil None null
  129. bool bool Boolean
  130. int int Short / Integer / Long / BigInteger
  131. float float Float / Double
  132. str str String
  133. bin bytes byte[]
  134. =========== ======= =======
  135. - Basic container types
  136. =========== ======= =======
  137. MessagePack Python Java
  138. =========== ======= =======
  139. array list Array
  140. =========== ======= =======
  141. - Ray builtin types
  142. - ActorHandle
  143. .. note::
  144. * Be aware of float / double precision between Python and Java. If Java use a
  145. float type to receive the input argument, the double precision Python data
  146. will be reduced to float precision in Java.
  147. * BigInteger can support max value of 2^64-1, please refer to:
  148. https://github.com/msgpack/msgpack/blob/master/spec.md#int-format-family.
  149. If the value larger than 2^64-1, then transfer the BigInteger:
  150. - From Java to Python: *raise an exception*
  151. - From Java to Java: **OK**
  152. The following example shows how to pass these types as parameters and how to
  153. return return these types.
  154. You can write a Python function which returns the input data:
  155. .. code-block:: python
  156. # ray_serialization.py
  157. import ray
  158. @ray.remote
  159. def py_return_input(v):
  160. return v
  161. Then you can transfer the object from Java to Python, then returns from Python
  162. to Java:
  163. .. code-block:: java
  164. package io.ray.demo;
  165. import io.ray.api.ObjectRef;
  166. import io.ray.api.Ray;
  167. import io.ray.api.function.PyFunction;
  168. import java.math.BigInteger;
  169. import org.testng.Assert;
  170. public class SerializationDemo {
  171. public static void main(String[] args) {
  172. Ray.init();
  173. Object[] inputs = new Object[]{
  174. true, // Boolean
  175. Byte.MAX_VALUE, // Byte
  176. Short.MAX_VALUE, // Short
  177. Integer.MAX_VALUE, // Integer
  178. Long.MAX_VALUE, // Long
  179. BigInteger.valueOf(Long.MAX_VALUE), // BigInteger
  180. "Hello World!", // String
  181. 1.234f, // Float
  182. 1.234, // Double
  183. "example binary".getBytes()}; // byte[]
  184. for (Object o : inputs) {
  185. ObjectRef res = Ray.task(
  186. PyFunction.of("ray_serialization", "py_return_input", o.getClass()),
  187. o).remote();
  188. Assert.assertEquals(res.get(), o);
  189. }
  190. Ray.shutdown();
  191. }
  192. }
  193. Cross-language exception stacks
  194. -------------------------------
  195. Suppose we have a Java package as follows:
  196. .. code-block:: java
  197. package io.ray.demo;
  198. import io.ray.api.ObjectRef;
  199. import io.ray.api.Ray;
  200. import io.ray.api.function.PyFunction;
  201. public class MyRayClass {
  202. public static int raiseExceptionFromPython() {
  203. PyFunction<Integer> raiseException = PyFunction.of(
  204. "ray_exception", "raise_exception", Integer.class);
  205. ObjectRef<Integer> refObj = Ray.task(raiseException).remote();
  206. return refObj.get();
  207. }
  208. }
  209. and a Python module as follows:
  210. .. code-block:: python
  211. # ray_exception.py
  212. import ray
  213. @ray.remote
  214. def raise_exception():
  215. 1 / 0
  216. Then, run the following code:
  217. .. code-block:: python
  218. # ray_exception_demo.py
  219. import ray
  220. ray.init(address="auto")
  221. obj_ref = ray.java_function(
  222. "io.ray.demo.MyRayClass",
  223. "raiseExceptionFromPython").remote()
  224. ray.get(obj_ref) # <-- raise exception from here.
  225. ray.shutdown()
  226. The exception stack will be:
  227. .. code-block:: text
  228. Traceback (most recent call last):
  229. File "ray_exception_demo.py", line 10, in <module>
  230. ray.get(obj_ref) # <-- raise exception from here.
  231. File "ray/worker.py", line 1425, in get
  232. raise value
  233. ray.exceptions.CrossLanguageError: An exception raised from JAVA:
  234. io.ray.runtime.exception.RayTaskException: (pid=92253, ip=10.15.239.68) Error executing task df5a1a828c9685d3ffffffff01000000
  235. at io.ray.runtime.task.TaskExecutor.execute(TaskExecutor.java:167)
  236. Caused by: io.ray.runtime.exception.CrossLanguageException: An exception raised from PYTHON:
  237. ray.exceptions.RayTaskError: ray::raise_exception() (pid=92252, ip=10.15.239.68)
  238. File "python/ray/_raylet.pyx", line 482, in ray._raylet.execute_task
  239. File "ray_exception.py", line 7, in raise_exception
  240. 1 / 0
  241. ZeroDivisionError: division by zero