This directory contains the java worker, with the following components. - java/api: Ray API definition - java/common: utilities - java/hook: binary rewrite of the Java byte-code for remote execution - java/runtime-common: common implementation of the runtime in worker - java/runtime-dev: a pure-java mock implementation of the runtime for fast development - java/runtime-native: a native implementation of the runtime - java/test: various tests - src/local\_scheduler/lib/java: JNI client library for local scheduler - src/plasma/lib/java: JNI client library for plasma storage Build and test ============== :: # build native components ../build.sh -l java # build java worker mvn clean install -Dmaven.test.skip # test export RAY_CONFIG=ray.config.ini mvn test Quick start =========== Starting Ray ------------ .. code:: java Ray.init(); Read and write remote objects ----------------------------- Each remote object is considered a ``RayObject`` where ``T`` is the type for this object. You can use ``Ray.put`` and ``RayObject.get`` to write and read the objects. .. code:: java Integer x = 1; RayObject obj = Ray.put(x); Integer x1 = obj.get(); assert (x.equals(x1)); Remote functions ---------------- Here is an ordinary java code piece for composing ``hello world example``. .. code:: java public class ExampleClass { public static void main(String[] args) { String str1 = add("hello", "world"); String str = add(str1, "example"); System.out.println(str); } public static String add(String a, String b) { return a + " " + b; } } We use ``@RayRemote`` to indicate that a function is remote, and use ``Ray.call`` to invoke it. The result from the latter is a ``RayObject`` where ``R`` is the return type of the target function. The following shows the changed example with ``add`` annotated, and correspondent calls executed on remote machines. .. code:: java public class ExampleClass { public static void main(String[] args) { Ray.init(); RayObject objStr1 = Ray.call(ExampleClass::add, "hello", "world"); RayObject objStr2 = Ray.call(ExampleClass::add, objStr1, "example"); String str = objStr2.get(); System.out.println(str); } @RayRemote public static String add(String a, String b) { return a + " " + b; } } Ray Java API ============ Basic API --------- ``Ray.init()`` ~~~~~~~~~~~~~~ Ray.init should be invoked before any other Ray functions to initialize the runtime. ``@RayRemote`` ~~~~~~~~~~~~~~ The annotation of ``@RayRemote`` can be used to decorate static java method or class. The former indicates that a target function is a remote function, which is valid with the follow requirements. \* it must be a public static method \* parameters and return value must not be the primitive type of java such as int, double, but could use the wrapper class like Integer,Double \* the return value of the method must always be the same with the same input When the annotation is used for classes, the classes are considered actors(a mechanism to share state among many remote functions). The member functions can be invoked using ``Ray.call``. The requirements for an actor class are as follows. \* it must have an constructor without any parameter \* if it is an inner class, it must be public static \* it must not have a member field or method decorated using ``public static``, as the semantic is undefined with multiple instances of this same class on different machines \* an actor method must be decorated using ``public`` but no ``static``, and the other requirements are the same as above. ``Ray.call`` ~~~~~~~~~~~~ .. code:: java RayObject call(Func func, ...); ``func`` is the target method, continued with appropriate parameters. There are some requirements here: - the return type of ``func`` must be ``R`` - currently at most 6 parameters of ``func`` are allowed - each parameter must be of type ``T`` of the correspondent ``func``'s parameter, or be the lifted ``RayObject`` to indicate a result from another ray call The returned object is labled as ``RayObject`` and its value will be put into memory of the machine where the function call is executed. ``Ray.put`` ~~~~~~~~~~~ You can also invoke ``Ray.put`` to explicitly place an object into local memory. .. code:: java public static RayObject put(T object); public static RayObject put(T obj, TM metadata); ``RayObject.get/getMeta`` ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. code:: java public class RayObject { public T get() throws TaskExecutionException; public M getMeta() throws TaskExecutionException; } This method blocks current thread until requested data gets ready and is fetched (if needed) from remote memory to local. ``Ray.wait`` ~~~~~~~~~~~~ Calling ``Ray.wait`` will block current thread and wait for specified ray calls. It returns when at least ``numReturns`` calls are completed, or the ``timeout`` expires. See multi-value support for ``RayList``. .. code:: java public static WaitResult wait(RayList waitfor, int numReturns, int timeout); public static WaitResult wait(RayList waitfor, int numReturns); public static WaitResult wait(RayList waitfor); Multi-value API --------------- Multi-value Types ~~~~~~~~~~~~~~~~~ Java worker supports multiple ``RayObject``\ s in a single data structure as a return value or a ray call parameter, through the following container types. ``MultipleReturnsX`` ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ There are multiple heterogeneous values, with their types as ``R0``, ``R1``,... respectively. Note currently this container type is only supported as the return type of a ray call, therefore you can not use it as the type of an input parameter. ``RayList`` '''''''''''''' A list of ``RayObject``, inherited from ``List`` in Java. It can be used as the type for both return value and parameters. ``RayMap`` '''''''''''''''' A map of ``RayObject`` with each indexed using a label with type ``L``, inherited from ``Map``. It can be used as the type for both return value and parameters. Enable multiple heterogeneous return values ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Java worker support at most four multiple heterogeneous return values, and in order to let the runtime know the number of return values we supply the method of ``Ray.call_X`` as follows. .. code:: java RayObjects2 call_2(Func func, ...); RayObjects3 call_3(Func func, ...); RayObjects4 call_4(Func func, ...); Note ``func`` must match the following requirements. - It must hava the return value of ``MultipleReturnsX``, and must be invoked using correspondent ``Ray.call_X`` Here is an example. .. code:: java public class MultiRExample { public static void main(String[] args) { Ray.init(); RayObjects2 refs = Ray.call_2(MultiRExample::sayMultiRet); Integer obj1 = refs.r0().get(); String obj2 = refs.r1().get(); Assert.assertTrue(obj1.equals(123)); Assert.assertTrue(obj2.equals("123")); } @RayRemote public static MultipleReturns2 sayMultiRet() { return new MultipleReturns2(123, "123"); } } Return with ``RayList`` ~~~~~~~~~~~~~~~~~~~~~~~ We use ``Ray.call_n`` to do so, which is similar to ``Ray.call`` except an additional parameter ``returnCount`` which tells the number of return ``RayObject`` in ``RayList``. This is because Ray core engines needs to know it before the method is really called. .. code:: java RayList call_n(Func func, Integer returnCount, ...); Here is an example. .. code:: java public class ListRExample { public static void main(String[] args) { Ray.init(); RayList ns = Ray.call_n(ListRExample::sayList, 10, 10); for (int i = 0; i < 10; i++) { RayObject obj = ns.Get(i); Assert.assertTrue(i == obj.get()); } } @RayRemote public static List sayList(Integer count) { ArrayList rets = new ArrayList<>(); for (int i = 0; i < count; i++) rets.add(i); return rets; } } Return with ``RayMap`` ~~~~~~~~~~~~~~~~~~~~~~ This is similar to ``RayList`` case, except that now each return ``RayObject`` in ``RayMap`` has a given label when ``Ray.call_n`` is made. .. code:: java RayMap call_n(Func func, Collection returnLabels, ...); Here is an example. .. code:: java public class MapRExample { public static void main(String[] args) { Ray.init(); RayMap ns = Ray.call_n(MapRExample::sayMap, Arrays.asList(1, 2, 4, 3), "n_futures_"); for (Entry> ne : ns.EntrySet()) { Integer key = ne.getKey(); RayObject obj = ne.getValue(); Assert.assertTrue(obj.get().equals("n_futures_" + key)); } } @RayRemote(externalIO = true) public static Map sayMap(Collection ids, String prefix) { Map ret = new HashMap<>(); for (int id : ids) { ret.put(id, prefix + id); } return ret; } } Enable ``RayList`` and ``RayMap`` as parameters ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. code:: java public class ListTExample { public static void main(String[] args) { Ray.init(); RayList ints = new RayList<>(); ints.add(Ray.put(new Integer(1))); ints.add(Ray.put(new Integer(1))); ints.add(Ray.put(new Integer(1))); RayObject obj = Ray.call(ListTExample::sayReadRayList, (List)ints); Assert.assertTrue(obj.get().equals(3)); } @RayRemote public static int sayReadRayList(List ints) { int sum = 0; for (Integer i : ints) { sum += i; } return sum; } } Actor Support ------------- Create Actors ~~~~~~~~~~~~~ A regular class annotated with ``@RayRemote`` is an actor class. .. code:: java @RayRemote public class Adder { public Adder() { sum = 0; } public Integer add(Integer n) { return sum += n; } private Integer sum; } Whenever you call ``Ray.create()`` method, an actor will be created, and you get a local ``RayActor`` of that actor as the return value. .. code:: java RayActor adder = Ray.create(Adder.class); Call Actor Methods ~~~~~~~~~~~~~~~~~~ The same ``Ray.call`` or its extended versions (e.g., ``Ray.call_n``) is applied, except that the first argument becomes ``RayActor``. .. code:: java RayObject Ray.call(Func func, RayActor actor, ...); RayObject result1 = Ray.call(Adder::add, adder, 1); RayObject result2 = Ray.call(Adder::add, adder, 10); result2.get(); // 11