[Java] Support dynamically defining resources when submitting task. (#3070)

## What do these changes do?
Before this PR, if we want to specify some resources, we must do as following codes:
```java
@RayRemote(Resources={ResourceItem("CPU", 10)})
public static void f1() {
// do sth
}

@RayRemote(Resources={ResourceItem("CPU", 10)})
class Demo {
// sth
}
```
Unfortunately, it's no way for us to create another actor or task with different resources required.

After this PR, the thing will be:
```java
ActorCreationOptions option = new ActorCreationOptions(); 
option.resources.put("CPU", 4.0);
RayActor<Echo> echo1 = Ray.createActor(Echo::new, option);
option.resources.put("Res-A", 4.0);
RayActor<Echo> echo2 = Ray.createActor(Echo::new, option);


//if we don't specify resource,  the resources will be `{"cpu":0.0}` by default.
Ray.call(Echo::echo, echo2, 100);
```


## Related issue number
N/A
This commit is contained in:
Wang Qing
2018-10-19 21:22:32 +08:00
committed by Hao Chen
parent 9d23fa03c9
commit b410ee0d29
11 changed files with 1433 additions and 399 deletions
@@ -12,6 +12,9 @@ import org.ray.api.RayObject;
import org.ray.api.WaitResult;
import org.ray.api.function.RayFunc;
import org.ray.api.id.UniqueId;
import org.ray.api.options.ActorCreationOptions;
import org.ray.api.options.BaseTaskOptions;
import org.ray.api.options.CallOptions;
import org.ray.api.runtime.RayRuntime;
import org.ray.runtime.config.RayConfig;
import org.ray.runtime.functionmanager.FunctionManager;
@@ -186,8 +189,8 @@ public abstract class AbstractRayRuntime implements RayRuntime {
}
@Override
public RayObject call(RayFunc func, Object[] args) {
TaskSpec spec = createTaskSpec(func, RayActorImpl.NIL, args, false);
public RayObject call(RayFunc func, Object[] args, CallOptions options) {
TaskSpec spec = createTaskSpec(func, RayActorImpl.NIL, args, false, options);
rayletClient.submitTask(spec);
return new RayObjectImpl(spec.returnIds[0]);
}
@@ -198,7 +201,7 @@ public abstract class AbstractRayRuntime implements RayRuntime {
throw new IllegalArgumentException("Unsupported actor type: " + actor.getClass().getName());
}
RayActorImpl actorImpl = (RayActorImpl)actor;
TaskSpec spec = createTaskSpec(func, actorImpl, args, false);
TaskSpec spec = createTaskSpec(func, actorImpl, args, false, null);
spec.getExecutionDependencies().add(((RayActorImpl) actor).getTaskCursor());
actorImpl.setTaskCursor(spec.returnIds[1]);
rayletClient.submitTask(spec);
@@ -207,8 +210,10 @@ public abstract class AbstractRayRuntime implements RayRuntime {
@Override
@SuppressWarnings("unchecked")
public <T> RayActor<T> createActor(RayFunc actorFactoryFunc, Object[] args) {
TaskSpec spec = createTaskSpec(actorFactoryFunc, RayActorImpl.NIL, args, true);
public <T> RayActor<T> createActor(RayFunc actorFactoryFunc,
Object[] args, ActorCreationOptions options) {
TaskSpec spec = createTaskSpec(actorFactoryFunc, RayActorImpl.NIL,
args, true, options);
RayActorImpl<?> actor = new RayActorImpl(spec.returnIds[0]);
actor.increaseTaskCounter();
actor.setTaskCursor(spec.returnIds[0]);
@@ -236,11 +241,10 @@ public abstract class AbstractRayRuntime implements RayRuntime {
* @return A TaskSpec object.
*/
private TaskSpec createTaskSpec(RayFunc func, RayActorImpl actor, Object[] args,
boolean isActorCreationTask) {
boolean isActorCreationTask, BaseTaskOptions taskOptions) {
final TaskSpec current = workerContext.getCurrentTask();
UniqueId taskId = rayletClient.generateTaskId(current.driverId,
current.taskId,
workerContext.nextCallIndex());
current.taskId, workerContext.nextCallIndex());
int numReturns = actor.getId().isNil() ? 1 : 2;
UniqueId[] returnIds = genReturnIds(taskId, numReturns);
@@ -249,6 +253,18 @@ public abstract class AbstractRayRuntime implements RayRuntime {
actorCreationId = returnIds[0];
}
Map<String, Double> resources;
if (null == taskOptions) {
resources = new HashMap<>();
} else {
resources = new HashMap<>(taskOptions.resources);
}
if (!resources.containsKey(ResourceUtil.CPU_LITERAL)
&& !resources.containsKey(ResourceUtil.CPU_LITERAL.toLowerCase())) {
resources.put(ResourceUtil.CPU_LITERAL, 0.0);
}
RayFunction rayFunction = functionManager.getFunction(current.driverId, func);
return new TaskSpec(
current.driverId,
@@ -261,7 +277,7 @@ public abstract class AbstractRayRuntime implements RayRuntime {
actor.increaseTaskCounter(),
ArgumentsBuilder.wrap(args),
returnIds,
ResourceUtil.getResourcesMapFromArray(rayFunction.getRayRemoteAnnotation()),
resources,
rayFunction.getFunctionDescriptor()
);
}
@@ -2,59 +2,11 @@ package org.ray.runtime.util;
import java.util.HashMap;
import java.util.Map;
import org.ray.api.annotation.RayRemote;
import org.ray.api.annotation.ResourceItem;
public class ResourceUtil {
public static final String CPU_LITERAL = "CPU";
public static final String GPU_LITERAL = "GPU";
/**
* Convert the array that contains resource items to a map.
*
* @param remoteAnnotation The RayRemote annotation that contains the resource items.
* @return The map whose key represents the resource name
* and the value represents the resource quantity.
*/
public static Map<String, Double> getResourcesMapFromArray(RayRemote remoteAnnotation) {
Map<String, Double> resourceMap = new HashMap<>();
if (remoteAnnotation != null) {
for (ResourceItem item : remoteAnnotation.resources()) {
if (!item.name().isEmpty()) {
resourceMap.put(item.name(), item.value());
}
}
}
if (!resourceMap.containsKey(CPU_LITERAL)) {
resourceMap.put(CPU_LITERAL, 0.0);
}
return resourceMap;
}
/**
* Convert the resources map to a format string.
*
* @param resources The resource map to be Converted.
* @return The format resources string, like "{CPU:4, GPU:0}".
*/
public static String getResourcesFromatStringFromMap(Map<String, Double> resources) {
if (resources == null) {
return "{}";
}
StringBuilder builder = new StringBuilder();
builder.append("{");
int count = 1;
for (Map.Entry<String, Double> entry : resources.entrySet()) {
builder.append(entry.getKey()).append(":").append(entry.getValue());
count++;
if (count != resources.size()) {
builder.append(", ");
}
}
builder.append("}");
return builder.toString();
}
/**
* Convert resources map to a string that is used
* for the command line argument of starting raylet.
@@ -99,7 +51,7 @@ public class ResourceUtil {
String[] resourcePair = trimItem.split(":");
if (resourcePair.length != 2) {
throw new IllegalArgumentException("Format of static resurces configure is invalid.");
throw new IllegalArgumentException("Format of static resources configure is invalid.");
}
final String resourceName = resourcePair[0].trim();
@@ -21,7 +21,17 @@ public class RayCallGenerator extends BaseGenerator {
newLine("");
newLine("package org.ray.api;");
newLine("");
newLine("import org.ray.api.function.*;");
newLine("import org.ray.api.function.RayFunc;");
newLine("import org.ray.api.function.RayFunc0;");
newLine("import org.ray.api.function.RayFunc1;");
newLine("import org.ray.api.function.RayFunc2;");
newLine("import org.ray.api.function.RayFunc3;");
newLine("import org.ray.api.function.RayFunc4;");
newLine("import org.ray.api.function.RayFunc5;");
newLine("import org.ray.api.function.RayFunc6;");
newLine("import org.ray.api.options.ActorCreationOptions;");
newLine("import org.ray.api.options.BaseTaskOptions;");
newLine("import org.ray.api.options.CallOptions;");
newLine("");
newLine("/**");
@@ -33,19 +43,21 @@ public class RayCallGenerator extends BaseGenerator {
newLine(1, "// Methods for remote function invocation.");
newLine(1, "// =======================================");
for (int i = 0; i <= MAX_PARAMETERS; i++) {
buildCalls(i, false, false);
buildCalls(i, false, false, false);
buildCalls(i, false, false, true);
}
newLine(1, "// ===========================================");
newLine(1, "// Methods for remote actor method invocation.");
newLine(1, "// ===========================================");
for (int i = 0; i <= MAX_PARAMETERS - 1; i++) {
buildCalls(i, true, false);
buildCalls(i, true, false, false);
}
newLine(1, "// ===========================");
newLine(1, "// Methods for actor creation.");
newLine(1, "// ===========================");
for (int i = 0; i <= MAX_PARAMETERS; i++) {
buildCalls(i, false, true);
buildCalls(i, false, true, false);
buildCalls(i, false, true, true);
}
newLine("}");
return sb.toString();
@@ -57,7 +69,8 @@ public class RayCallGenerator extends BaseGenerator {
* @param forActor build actor api when true, otherwise build task api.
* @param forActorCreation build `Ray.createActor` when true, otherwise build `Ray.call`.
*/
private void buildCalls(int numParameters, boolean forActor, boolean forActorCreation) {
private void buildCalls(int numParameters, boolean forActor,
boolean forActorCreation, boolean hasOptionsParam) {
String genericTypes = "";
String argList = "";
for (int i = 0; i < numParameters; i++) {
@@ -82,18 +95,36 @@ public class RayCallGenerator extends BaseGenerator {
paramPrefix += ", ";
}
String optionsParam;
if (hasOptionsParam) {
optionsParam = forActorCreation ? ", ActorCreationOptions options" : ", CallOptions options";
} else {
optionsParam = "";
}
String optionsArg;
if (forActor) {
optionsArg = "";
} else {
if (hasOptionsParam) {
optionsArg = ", options";
} else {
optionsArg = ", null";
}
}
String returnType = !forActorCreation ? "RayObject<R>" : "RayActor<A>";
String funcName = !forActorCreation ? "call" : "createActor";
String funcArgs = !forActor ? "f, args" : "f, actor, args";
for (String param : generateParameters(0, numParameters)) {
// method signature
newLine(1, String.format(
"public static <%s> %s %s(%s) {",
genericTypes, returnType, funcName, paramPrefix + param
"public static <%s> %s %s(%s%s) {",
genericTypes, returnType, funcName, paramPrefix + param, optionsParam
));
// method body
newLine(2, String.format("Object[] args = new Object[]{%s};", argList));
newLine(2, String.format("return Ray.internal().%s(%s);", funcName, funcArgs));
newLine(2, String.format("return Ray.internal().%s(%s%s);", funcName, funcArgs, optionsArg));
newLine(1, "}");
}
}