Terminology change Object Reference -> Object ID (#330)

This commit is contained in:
Robert Nishihara
2016-07-31 19:58:03 -07:00
committed by Philipp Moritz
parent a89aa30f24
commit 98a508d6ca
36 changed files with 982 additions and 984 deletions
+1 -1
View File
@@ -23,5 +23,5 @@ import libraylib as lib
import serialization
from worker import scheduler_info, visualize_computation_graph, task_info, register_module, init, connect, disconnect, get, put, remote, kill_workers, restart_workers_local
from worker import Reusable, reusables
from libraylib import ObjRef
from libraylib import ObjectID
import internal
+31 -31
View File
@@ -9,20 +9,20 @@ __all__ = ["BLOCK_SIZE", "DistArray", "assemble", "zeros", "ones", "copy",
BLOCK_SIZE = 10
class DistArray(object):
def construct(self, shape, objrefs=None):
def construct(self, shape, objectids=None):
self.shape = shape
self.ndim = len(shape)
self.num_blocks = [int(np.ceil(1.0 * a / BLOCK_SIZE)) for a in self.shape]
self.objrefs = objrefs if objrefs is not None else np.empty(self.num_blocks, dtype=object)
if self.num_blocks != list(self.objrefs.shape):
raise Exception("The fields `num_blocks` and `objrefs` are inconsistent, `num_blocks` is {} and `objrefs` has shape {}".format(self.num_blocks, list(self.objrefs.shape)))
self.objectids = objectids if objectids is not None else np.empty(self.num_blocks, dtype=object)
if self.num_blocks != list(self.objectids.shape):
raise Exception("The fields `num_blocks` and `objectids` are inconsistent, `num_blocks` is {} and `objectids` has shape {}".format(self.num_blocks, list(self.objectids.shape)))
def deserialize(self, primitives):
(shape, objrefs) = primitives
self.construct(shape, objrefs)
(shape, objectids) = primitives
self.construct(shape, objectids)
def serialize(self):
return (self.shape, self.objrefs)
return (self.shape, self.objectids)
def __init__(self, shape=None):
if shape is not None:
@@ -54,14 +54,14 @@ class DistArray(object):
return [int(np.ceil(1.0 * a / BLOCK_SIZE)) for a in shape]
def assemble(self):
"""Assemble an array on this node from a distributed array object reference."""
first_block = ray.get(self.objrefs[(0,) * self.ndim])
"""Assemble an array on this node from a distributed array of object IDs."""
first_block = ray.get(self.objectids[(0,) * self.ndim])
dtype = first_block.dtype
result = np.zeros(self.shape, dtype=dtype)
for index in np.ndindex(*self.num_blocks):
lower = DistArray.compute_block_lower(index, self.shape)
upper = DistArray.compute_block_upper(index, self.shape)
result[[slice(l, u) for (l, u) in zip(lower, upper)]] = ray.get(self.objrefs[index])
result[[slice(l, u) for (l, u) in zip(lower, upper)]] = ray.get(self.objectids[index])
return result
def __getitem__(self, sliced):
@@ -80,28 +80,28 @@ def numpy_to_dist(a):
for index in np.ndindex(*result.num_blocks):
lower = DistArray.compute_block_lower(index, a.shape)
upper = DistArray.compute_block_upper(index, a.shape)
result.objrefs[index] = ray.put(a[[slice(l, u) for (l, u) in zip(lower, upper)]])
result.objectids[index] = ray.put(a[[slice(l, u) for (l, u) in zip(lower, upper)]])
return result
@ray.remote([List, str], [DistArray])
def zeros(shape, dtype_name="float"):
result = DistArray(shape)
for index in np.ndindex(*result.num_blocks):
result.objrefs[index] = ra.zeros.remote(DistArray.compute_block_shape(index, shape), dtype_name=dtype_name)
result.objectids[index] = ra.zeros.remote(DistArray.compute_block_shape(index, shape), dtype_name=dtype_name)
return result
@ray.remote([List, str], [DistArray])
def ones(shape, dtype_name="float"):
result = DistArray(shape)
for index in np.ndindex(*result.num_blocks):
result.objrefs[index] = ra.ones.remote(DistArray.compute_block_shape(index, shape), dtype_name=dtype_name)
result.objectids[index] = ra.ones.remote(DistArray.compute_block_shape(index, shape), dtype_name=dtype_name)
return result
@ray.remote([DistArray], [DistArray])
def copy(a):
result = DistArray(a.shape)
for index in np.ndindex(*result.num_blocks):
result.objrefs[index] = a.objrefs[index] # We don't need to actually copy the objects because cluster-level objects are assumed to be immutable.
result.objectids[index] = a.objectids[index] # We don't need to actually copy the objects because cluster-level objects are assumed to be immutable.
return result
@ray.remote([int, int, str], [DistArray])
@@ -112,9 +112,9 @@ def eye(dim1, dim2=-1, dtype_name="float"):
for (i, j) in np.ndindex(*result.num_blocks):
block_shape = DistArray.compute_block_shape([i, j], shape)
if i == j:
result.objrefs[i, j] = ra.eye.remote(block_shape[0], block_shape[1], dtype_name=dtype_name)
result.objectids[i, j] = ra.eye.remote(block_shape[0], block_shape[1], dtype_name=dtype_name)
else:
result.objrefs[i, j] = ra.zeros.remote(block_shape, dtype_name=dtype_name)
result.objectids[i, j] = ra.zeros.remote(block_shape, dtype_name=dtype_name)
return result
@ray.remote([DistArray], [DistArray])
@@ -124,11 +124,11 @@ def triu(a):
result = DistArray(a.shape)
for (i, j) in np.ndindex(*result.num_blocks):
if i < j:
result.objrefs[i, j] = ra.copy.remote(a.objrefs[i, j])
result.objectids[i, j] = ra.copy.remote(a.objectids[i, j])
elif i == j:
result.objrefs[i, j] = ra.triu.remote(a.objrefs[i, j])
result.objectids[i, j] = ra.triu.remote(a.objectids[i, j])
else:
result.objrefs[i, j] = ra.zeros_like.remote(a.objrefs[i, j])
result.objectids[i, j] = ra.zeros_like.remote(a.objectids[i, j])
return result
@ray.remote([DistArray], [DistArray])
@@ -138,11 +138,11 @@ def tril(a):
result = DistArray(a.shape)
for (i, j) in np.ndindex(*result.num_blocks):
if i > j:
result.objrefs[i, j] = ra.copy.remote(a.objrefs[i, j])
result.objectids[i, j] = ra.copy.remote(a.objectids[i, j])
elif i == j:
result.objrefs[i, j] = ra.tril.remote(a.objrefs[i, j])
result.objectids[i, j] = ra.tril.remote(a.objectids[i, j])
else:
result.objrefs[i, j] = ra.zeros_like.remote(a.objrefs[i, j])
result.objectids[i, j] = ra.zeros_like.remote(a.objectids[i, j])
return result
@ray.remote([np.ndarray], [np.ndarray])
@@ -167,8 +167,8 @@ def dot(a, b):
shape = [a.shape[0], b.shape[1]]
result = DistArray(shape)
for (i, j) in np.ndindex(*result.num_blocks):
args = list(a.objrefs[i, :]) + list(b.objrefs[:, j])
result.objrefs[i, j] = blockwise_dot.remote(*args)
args = list(a.objectids[i, :]) + list(b.objectids[:, j])
result.objectids[i, j] = blockwise_dot.remote(*args)
return result
@ray.remote([DistArray, List], [DistArray])
@@ -176,9 +176,9 @@ def subblocks(a, *ranges):
"""
This function produces a distributed array from a subset of the blocks in the `a`. The result and `a` will have the same number of dimensions.For example,
subblocks(a, [0, 1], [2, 4])
will produce a DistArray whose objrefs are
[[a.objrefs[0, 2], a.objrefs[0, 4]],
[a.objrefs[1, 2], a.objrefs[1, 4]]]
will produce a DistArray whose objectids are
[[a.objectids[0, 2], a.objectids[0, 4]],
[a.objectids[1, 2], a.objectids[1, 4]]]
We allow the user to pass in an empty list [] to indicate the full range.
"""
ranges = list(ranges)
@@ -198,7 +198,7 @@ def subblocks(a, *ranges):
shape = [(len(ranges[i]) - 1) * BLOCK_SIZE + last_block_shape[i] for i in range(a.ndim)]
result = DistArray(shape)
for index in np.ndindex(*result.num_blocks):
result.objrefs[index] = a.objrefs[tuple([ranges[i][index[i]] for i in range(a.ndim)])]
result.objectids[index] = a.objectids[tuple([ranges[i][index[i]] for i in range(a.ndim)])]
return result
@ray.remote([DistArray], [DistArray])
@@ -208,7 +208,7 @@ def transpose(a):
result = DistArray([a.shape[1], a.shape[0]])
for i in range(result.num_blocks[0]):
for j in range(result.num_blocks[1]):
result.objrefs[i, j] = ra.transpose.remote(a.objrefs[j, i])
result.objectids[i, j] = ra.transpose.remote(a.objectids[j, i])
return result
# TODO(rkn): support broadcasting?
@@ -218,7 +218,7 @@ def add(x1, x2):
raise Exception("add expects arguments `x1` and `x2` to have the same shape, but x1.shape = {}, and x2.shape = {}.".format(x1.shape, x2.shape))
result = DistArray(x1.shape)
for index in np.ndindex(*result.num_blocks):
result.objrefs[index] = ra.add.remote(x1.objrefs[index], x2.objrefs[index])
result.objectids[index] = ra.add.remote(x1.objectids[index], x2.objectids[index])
return result
# TODO(rkn): support broadcasting?
@@ -228,5 +228,5 @@ def subtract(x1, x2):
raise Exception("subtract expects arguments `x1` and `x2` to have the same shape, but x1.shape = {}, and x2.shape = {}.".format(x1.shape, x2.shape))
result = DistArray(x1.shape)
for index in np.ndindex(*result.num_blocks):
result.objrefs[index] = ra.subtract.remote(x1.objrefs[index], x2.objrefs[index])
result.objectids[index] = ra.subtract.remote(x1.objectids[index], x2.objectids[index])
return result
+16 -16
View File
@@ -32,7 +32,7 @@ def tsqr(a):
q_tree = np.empty((num_blocks, K), dtype=object)
current_rs = []
for i in range(num_blocks):
block = a.objrefs[i, 0]
block = a.objectids[i, 0]
q, r = ra.linalg.qr.remote(block)
q_tree[i, 0] = q
current_rs.append(r)
@@ -57,8 +57,8 @@ def tsqr(a):
q_shape = [a.shape[0], a.shape[0]]
q_num_blocks = DistArray.compute_num_blocks(q_shape)
q_result = DistArray()
q_objrefs = np.empty(q_num_blocks, dtype=object)
q_result.construct(q_shape, q_objrefs)
q_objectids = np.empty(q_num_blocks, dtype=object)
q_result.construct(q_shape, q_objectids)
# reconstruct output
for i in range(num_blocks):
@@ -73,7 +73,7 @@ def tsqr(a):
upper = [2 * a.shape[1], BLOCK_SIZE]
ith_index /= 2
q_block_current = ra.dot.remote(q_block_current, ra.subarray.remote(q_tree[ith_index, j], lower, upper))
q_result.objrefs[i] = q_block_current
q_result.objectids[i] = q_block_current
r = current_rs[0]
return q_result, r
@@ -126,7 +126,7 @@ def tsqr_hr(a):
q, r_temp = tsqr.remote(a)
y, u, s = modified_lu.remote(q)
y_blocked = ray.get(y)
t, y_top = tsqr_hr_helper1.remote(u, s, y_blocked.objrefs[0, 0], a.shape[1])
t, y_top = tsqr_hr_helper1.remote(u, s, y_blocked.objectids[0, 0], a.shape[1])
r = tsqr_hr_helper2.remote(s, r_temp)
return y, t, y_top, r
@@ -146,9 +146,9 @@ def qr(a):
# we will store our scratch work in a_work
a_work = DistArray()
a_work.construct(a.shape, np.copy(a.objrefs))
a_work.construct(a.shape, np.copy(a.objectids))
result_dtype = np.linalg.qr(ray.get(a.objrefs[0, 0]))[0].dtype.name
result_dtype = np.linalg.qr(ray.get(a.objectids[0, 0]))[0].dtype.name
r_res = ray.get(zeros.remote([k, n], result_dtype)) # TODO(rkn): It would be preferable not to get this right after creating it.
y_res = ray.get(zeros.remote([m, k], result_dtype)) # TODO(rkn): It would be preferable not to get this right after creating it.
Ts = []
@@ -159,27 +159,27 @@ def qr(a):
y_val = ray.get(y)
for j in range(i, a.num_blocks[0]):
y_res.objrefs[j, i] = y_val.objrefs[j - i, 0]
y_res.objectids[j, i] = y_val.objectids[j - i, 0]
if a.shape[0] > a.shape[1]:
# in this case, R needs to be square
R_shape = ray.get(ra.shape.remote(R))
eye_temp = ra.eye.remote(R_shape[1], R_shape[0], dtype_name=result_dtype)
r_res.objrefs[i, i] = ra.dot.remote(eye_temp, R)
r_res.objectids[i, i] = ra.dot.remote(eye_temp, R)
else:
r_res.objrefs[i, i] = R
r_res.objectids[i, i] = R
Ts.append(numpy_to_dist.remote(t))
for c in range(i + 1, a.num_blocks[1]):
W_rcs = []
for r in range(i, a.num_blocks[0]):
y_ri = y_val.objrefs[r - i, 0]
W_rcs.append(qr_helper2.remote(y_ri, a_work.objrefs[r, c]))
y_ri = y_val.objectids[r - i, 0]
W_rcs.append(qr_helper2.remote(y_ri, a_work.objectids[r, c]))
W_c = ra.sum_list.remote(*W_rcs)
for r in range(i, a.num_blocks[0]):
y_ri = y_val.objrefs[r - i, 0]
A_rc = qr_helper1.remote(a_work.objrefs[r, c], y_ri, t, W_c)
a_work.objrefs[r, c] = A_rc
r_res.objrefs[i, c] = a_work.objrefs[i, c]
y_ri = y_val.objectids[r - i, 0]
A_rc = qr_helper1.remote(a_work.objectids[r, c], y_ri, t, W_c)
a_work.objectids[r, c] = A_rc
r_res.objectids[i, c] = a_work.objectids[i, c]
# construct q_res from Ys and Ts
q = eye.remote(m, k, dtype_name=result_dtype)
+3 -3
View File
@@ -9,9 +9,9 @@ from core import *
@ray.remote([List], [DistArray])
def normal(shape):
num_blocks = DistArray.compute_num_blocks(shape)
objrefs = np.empty(num_blocks, dtype=object)
objectids = np.empty(num_blocks, dtype=object)
for index in np.ndindex(*num_blocks):
objrefs[index] = ra.random.normal.remote(DistArray.compute_block_shape(index, shape))
objectids[index] = ra.random.normal.remote(DistArray.compute_block_shape(index, shape))
result = DistArray()
result.construct(shape, objrefs)
result.construct(shape, objectids)
return result
+3 -3
View File
@@ -22,13 +22,13 @@ def graph_to_graphviz(computation_graph):
dot.edge("op" + str(i), str(res))
elif op.HasField("put"):
dot.node("op" + str(i), shape="box", label=str(i) + "\n" + "put")
dot.edge("op" + str(i), str(op.put.objref))
dot.edge("op" + str(i), str(op.put.objectid))
elif op.HasField("get"):
dot.node("op" + str(i), shape="box", label=str(i) + "\n" + "get")
creator_operationid = op.creator_operationid if op.creator_operationid != 2 ** 64 - 1 else "-root"
dot.edge("op" + str(creator_operationid), "op" + str(i), style="dotted", constraint="false")
for arg in op.task.arg:
if not arg.HasField("obj"):
dot.node(str(arg.ref))
dot.edge(str(arg.ref), "op" + str(i))
dot.node(str(arg.id))
dot.edge(str(arg.id), "op" + str(i))
return dot
+6 -6
View File
@@ -55,18 +55,18 @@ def is_arrow_serializable(value):
def serialize(worker_capsule, obj):
primitive_obj = to_primitive(obj)
obj_capsule, contained_objrefs = ray.lib.serialize_object(worker_capsule, primitive_obj) # contained_objrefs is a list of the objrefs contained in obj
return obj_capsule, contained_objrefs
obj_capsule, contained_objectids = ray.lib.serialize_object(worker_capsule, primitive_obj) # contained_objectids is a list of the objectids contained in obj
return obj_capsule, contained_objectids
def deserialize(worker_capsule, capsule):
primitive_obj = ray.lib.deserialize_object(worker_capsule, capsule)
return from_primitive(primitive_obj)
def serialize_task(worker_capsule, func_name, args):
primitive_args = [(arg if isinstance(arg, ray.lib.ObjRef) else to_primitive(arg)) for arg in args]
primitive_args = [(arg if isinstance(arg, ray.ObjectID) else to_primitive(arg)) for arg in args]
return ray.lib.serialize_task(worker_capsule, func_name, primitive_args)
def deserialize_task(worker_capsule, task):
func_name, primitive_args, return_objrefs = task
args = [(arg if isinstance(arg, ray.lib.ObjRef) else from_primitive(arg)) for arg in primitive_args]
return func_name, args, return_objrefs
func_name, primitive_args, return_objectids = task
args = [(arg if isinstance(arg, ray.ObjectID) else from_primitive(arg)) for arg in primitive_args]
return func_name, args, return_objectids
+74 -75
View File
@@ -75,12 +75,12 @@ class RayFailedObject(object):
class RayDealloc(object):
"""An object used internally to properly implement reference counting.
When we call get_object with a particular object reference, we create a
RayDealloc object with the information necessary to properly handle closing
the relevant memory segment when the object is no longer needed by the worker.
The RayDealloc object is stored as a field in the object returned by
get_object so that its destructor is only called when the worker no longer has
any references to the object.
When we call get_object with a particular object ID, we create a RayDealloc
object with the information necessary to properly handle closing the relevant
memory segment when the object is no longer needed by the worker. The
RayDealloc object is stored as a field in the object returned by get_object so
that its destructor is only called when the worker no longer has any
references to the object.
Attributes
handle (worker capsule): A Python object wrapping a C++ Worker object.
@@ -293,14 +293,14 @@ class Worker(object):
self.mode = mode
colorama.init()
def put_object(self, objref, value):
"""Put value in the local object store with object reference objref.
def put_object(self, objectid, value):
"""Put value in the local object store with object id objectid.
This assumes that the value for objref has not yet been placed in the
This assumes that the value for objectid has not yet been placed in the
local object store.
Args:
objref (ray.ObjRef): The object reference of the value to be put.
objectid (ray.ObjectID): The object ID of the value to be put.
value (serializable object): The value to put in the object store.
"""
try:
@@ -312,7 +312,7 @@ class Worker(object):
# the len(schema) is for storing the metadata and the 4096 is for storing
# the metadata in the batch (see INITIAL_METADATA_SIZE in arrow)
size = size + 8 + len(schema) + 4096
buff, segmentid = ray.lib.allocate_buffer(self.handle, objref, size)
buff, segmentid = ray.lib.allocate_buffer(self.handle, objectid, size)
# write the metadata length
np.frombuffer(buff, dtype="int64", count=1)[0] = len(schema)
# metadata buffer
@@ -321,25 +321,25 @@ class Worker(object):
metadata[:] = schema
data = np.frombuffer(buff, dtype="byte")[8 + len(schema):]
metadata_offset = libnumbuf.write_to_buffer(serialized, memoryview(data))
ray.lib.finish_buffer(self.handle, objref, segmentid, metadata_offset)
ray.lib.finish_buffer(self.handle, objectid, segmentid, metadata_offset)
except:
# At the moment, custom object and objects that contain object references take this path
# At the moment, custom object and objects that contain object IDs take this path
# TODO(pcm): Make sure that these are the only objects getting serialized to protobuf
object_capsule, contained_objrefs = serialization.serialize(self.handle, value) # contained_objrefs is a list of the objrefs contained in object_capsule
ray.lib.put_object(self.handle, objref, object_capsule, contained_objrefs)
object_capsule, contained_objectids = serialization.serialize(self.handle, value) # contained_objectids is a list of the objectids contained in object_capsule
ray.lib.put_object(self.handle, objectid, object_capsule, contained_objectids)
def get_object(self, objref):
"""Get the value in the local object store associated with objref.
def get_object(self, objectid):
"""Get the value in the local object store associated with objectid.
Return the value from the local object store for objref. This will block
until the value for objref has been written to the local object store.
Return the value from the local object store for objectid. This will block
until the value for objectid has been written to the local object store.
Args:
objref (ray.ObjRef): The object reference of the value to retrieve.
objectid (ray.ObjectID): The object ID of the value to retrieve.
"""
if ray.lib.is_arrow(self.handle, objref):
if ray.lib.is_arrow(self.handle, objectid):
## this is the new codepath
buff, segmentid, metadata_offset = ray.lib.get_buffer(self.handle, objref)
buff, segmentid, metadata_offset = ray.lib.get_buffer(self.handle, objectid)
metadata_size = np.frombuffer(buff, dtype="int64", count=1)[0]
metadata = np.frombuffer(buff, dtype="byte", offset=8, count=metadata_size)
data = np.frombuffer(buff, dtype="byte")[8 + metadata_size:]
@@ -349,9 +349,9 @@ class Worker(object):
assert len(deserialized) == 1
result = deserialized[0]
## this is the old codepath
# result, segmentid = ray.lib.get_arrow(self.handle, objref)
# result, segmentid = ray.lib.get_arrow(self.handle, objectid)
else:
object_capsule, segmentid = ray.lib.get_object(self.handle, objref)
object_capsule, segmentid = ray.lib.get_object(self.handle, objectid)
result = serialization.deserialize(self.handle, object_capsule)
if isinstance(result, int):
@@ -379,13 +379,13 @@ class Worker(object):
elif result == None:
ray.lib.unmap_object(self.handle, segmentid) # need to unmap here because result is passed back "by value" and we have no reference to unmap later
return None # can't subclass None and don't need to because there is a global None
result.ray_objref = objref # TODO(pcm): This could be done only for the "get" case in the future if we want to increase performance
result.ray_objectid = objectid # TODO(pcm): This could be done only for the "get" case in the future if we want to increase performance
result.ray_deallocator = RayDealloc(self.handle, segmentid)
return result
def alias_objrefs(self, alias_objref, target_objref):
"""Make two object references refer to the same object."""
ray.lib.alias_objrefs(self.handle, alias_objref, target_objref)
def alias_objectids(self, alias_objectid, target_objectid):
"""Make two object IDs refer to the same object."""
ray.lib.alias_objectids(self.handle, alias_objectid, target_objectid)
def register_function(self, function):
"""Register a function with the scheduler.
@@ -405,20 +405,20 @@ class Worker(object):
"""Submit a remote task to the scheduler.
Tell the scheduler to schedule the execution of the function with name
func_name with arguments args. Retrieve object references for the outputs of
func_name with arguments args. Retrieve object IDs for the outputs of
the function from the scheduler and immediately return them.
Args:
func_name (str): The name of the function to be executed.
args (List[Any]): The arguments to pass into the function. Arguments can
be object references or they can be values. If they are values, they
be object IDs or they can be values. If they are values, they
must be serializable objecs.
"""
task_capsule = serialization.serialize_task(self.handle, func_name, args)
objrefs = ray.lib.submit_task(self.handle, task_capsule)
objectids = ray.lib.submit_task(self.handle, task_capsule)
if self.mode in [ray.SHELL_MODE, ray.SCRIPT_MODE]:
print_task_info(ray.lib.task_info(self.handle), self.mode)
return objrefs
return objectids
global_worker = Worker()
"""Worker: The global Worker object for this worker process.
@@ -645,29 +645,29 @@ def disconnect(worker=global_worker):
worker.cached_remote_functions = []
reusables._cached_reusables = []
def get(objref, worker=global_worker):
def get(objectid, worker=global_worker):
"""Get a remote object from an object store.
This method blocks until the object corresponding to objref is available in
This method blocks until the object corresponding to objectid is available in
the local object store. If this object is not in the local object store, it
will be shipped from an object store that has it (once the object has been
created).
Args:
objref (ray.ObjRef): Object reference to the object to get.
objectid (ray.ObjectID): Object ID to the object to get.
Returns:
A Python object
"""
check_connected(worker)
if worker.mode == ray.PYTHON_MODE:
return objref # In ray.PYTHON_MODE, ray.get is the identity operation (the input will actually be a value not an objref)
ray.lib.request_object(worker.handle, objref)
return objectid # In ray.PYTHON_MODE, ray.get is the identity operation (the input will actually be a value not an objectid)
ray.lib.request_object(worker.handle, objectid)
if worker.mode in [ray.SHELL_MODE, ray.SCRIPT_MODE]:
print_task_info(ray.lib.task_info(worker.handle), worker.mode)
value = worker.get_object(objref)
value = worker.get_object(objectid)
if isinstance(value, RayFailedObject):
raise Exception("The task that created this object reference failed with error message:\n{}".format(value.error_message))
raise Exception("The task that created this object ID failed with error message:\n{}".format(value.error_message))
return value
def put(value, worker=global_worker):
@@ -677,16 +677,16 @@ def put(value, worker=global_worker):
value (serializable object): The Python object to be stored.
Returns:
The object reference assigned to this value.
The object ID assigned to this value.
"""
check_connected(worker)
if worker.mode == ray.PYTHON_MODE:
return value # In ray.PYTHON_MODE, ray.put is the identity operation
objref = ray.lib.get_objref(worker.handle)
worker.put_object(objref, value)
objectid = ray.lib.get_objectid(worker.handle)
worker.put_object(objectid, value)
if worker.mode in [ray.SHELL_MODE, ray.SCRIPT_MODE]:
print_task_info(ray.lib.task_info(worker.handle), worker.mode)
return objref
return objectid
def kill_workers(worker=global_worker):
"""Kill all of the workers in the cluster. This does not kill drivers.
@@ -748,7 +748,7 @@ def main_loop(worker=global_worker):
This method is an infinite loop. It waits to receive tasks from the scheduler.
When it receives a task, it first deserializes the task. Then it retrieves the
values for any arguments that were passed in as object references. Then it
values for any arguments that were passed in as object IDs. Then it
passes the arguments to the actual function. Then it stores the outputs of the
function in the local object store. Then it notifies the scheduler that it
completed the task.
@@ -763,22 +763,22 @@ def main_loop(worker=global_worker):
raise Exception("Worker is attempting to enter main_loop but has not been connected yet.")
ray.lib.start_worker_service(worker.handle)
def process_task(task): # wrapping these lines in a function should cause the local variables to go out of scope more quickly, which is useful for inspecting reference counts
func_name, args, return_objrefs = serialization.deserialize_task(worker.handle, task)
func_name, args, return_objectids = serialization.deserialize_task(worker.handle, task)
try:
arguments = get_arguments_for_execution(worker.functions[func_name], args, worker) # get args from objstore
outputs = worker.functions[func_name].executor(arguments) # execute the function
if len(return_objrefs) == 1:
if len(return_objectids) == 1:
outputs = (outputs,)
except Exception:
exception_message = format_error_message(traceback.format_exc())
# Here we are storing RayFailedObjects in the object store to indicate
# failure (this is only interpreted by the worker).
failure_objects = [RayFailedObject(exception_message) for _ in range(len(return_objrefs))]
store_outputs_in_objstore(return_objrefs, failure_objects, worker)
failure_objects = [RayFailedObject(exception_message) for _ in range(len(return_objectids))]
store_outputs_in_objstore(return_objectids, failure_objects, worker)
ray.lib.notify_task_completed(worker.handle, False, exception_message) # notify the scheduler that the task threw an exception
_logger().info("Worker threw exception with message: \n\n{}\n, while running function {}.".format(exception_message, func_name))
else:
store_outputs_in_objstore(return_objrefs, outputs, worker) # store output in local object store
store_outputs_in_objstore(return_objectids, outputs, worker) # store output in local object store
ray.lib.notify_task_completed(worker.handle, True, "") # notify the scheduler that the task completed successfully
finally:
# Reinitialize the values of reusable variables that were used in the task
@@ -868,11 +868,11 @@ def remote(arg_types, return_types, worker=global_worker):
# match the usual behavior of immutable remote objects.
return func(*copy.deepcopy(args))
check_arguments(arg_types, has_vararg_param, func_name, args) # throws an exception if args are invalid
objrefs = _submit_task(func_name, args)
if len(objrefs) == 1:
return objrefs[0]
elif len(objrefs) > 1:
return objrefs
objectids = _submit_task(func_name, args)
if len(objectids) == 1:
return objectids[0]
elif len(objectids) > 1:
return objectids
def func_executor(arguments):
"""This gets run when the remote function is executed."""
_logger().info("Calling function {}".format(func.__name__))
@@ -977,8 +977,8 @@ def check_return_values(function, result):
# Here we do some limited type checking to make sure the return values have
# the right types.
for i in range(len(result)):
if (not issubclass(type(result[i]), function.return_types[i])) and (not isinstance(result[i], ray.lib.ObjRef)):
raise Exception("The {}th return value for function {} has type {}, but the @remote decorator expected a return value of type {} or an ObjRef.".format(i, function.__name__, type(result[i]), function.return_types[i]))
if (not issubclass(type(result[i]), function.return_types[i])) and (not isinstance(result[i], ray.lib.ObjectID)):
raise Exception("The {}th return value for function {} has type {}, but the @remote decorator expected a return value of type {} or an ObjectID.".format(i, function.__name__, type(result[i]), function.return_types[i]))
def typecheck_arg(arg, expected_type, i, name):
"""Check that an argument has the expected type.
@@ -1033,8 +1033,8 @@ def check_arguments(arg_types, has_vararg_param, name, args):
else:
assert False, "This code should be unreachable."
if isinstance(arg, ray.lib.ObjRef):
# TODO(rkn): When we have type information in the ObjRef, do type checking here.
if isinstance(arg, ray.ObjectID):
# TODO(rkn): When we have type information in the ObjectID, do type checking here.
pass
else:
typecheck_arg(arg, expected_type, i, name)
@@ -1043,9 +1043,9 @@ def get_arguments_for_execution(function, args, worker=global_worker):
"""Retrieve the arguments for the remote function.
This retrieves the values for the arguments to the remote function that were
passed in as object references. Argumens that were passed by value are not
changed. This also does some type checking. This is called by the worker that
is executing the remote function.
passed in as object IDs. Argumens that were passed by value are not changed.
This also does some type checking. This is called by the worker that is
executing the remote function.
Args:
function (Callable): The remote function whose arguments are being
@@ -1075,7 +1075,7 @@ def get_arguments_for_execution(function, args, worker=global_worker):
else:
assert False, "This code should be unreachable."
if isinstance(arg, ray.lib.ObjRef):
if isinstance(arg, ray.ObjectID):
# get the object from the local object store
_logger().info("Getting argument {} for function {}.".format(i, function.__name__))
argument = worker.get_object(arg)
@@ -1088,31 +1088,30 @@ def get_arguments_for_execution(function, args, worker=global_worker):
arguments.append(argument)
return arguments
def store_outputs_in_objstore(objrefs, outputs, worker=global_worker):
def store_outputs_in_objstore(objectids, outputs, worker=global_worker):
"""Store the outputs of a remote function in the local object store.
This stores the values that were returned by a remote function in the local
object store. If any of the return values are object references, then these
object references are aliased with the object references that the scheduler
assigned for the return values. This is called by the worker that executes the
remote function.
object store. If any of the return values are object IDs, then these object
IDs are aliased with the object IDs that the scheduler assigned for the return
values. This is called by the worker that executes the remote function.
Note:
The arguments objrefs and outputs should have the same length.
The arguments objectids and outputs should have the same length.
Args:
objrefs (List[ray.ObjRef]): The object references that were assigned to the
objectids (List[ray.ObjectID]): The object IDs that were assigned to the
outputs of the remote function call.
outputs (Tuple): The value returned by the remote function. If the remote
function was supposed to only return one value, then its output was
wrapped in a tuple with one element prior to being passed into this
function.
"""
for i in range(len(objrefs)):
if isinstance(outputs[i], ray.lib.ObjRef):
# An ObjRef is being returned, so we must alias objrefs[i] so that it refers to the same object that outputs[i] refers to
_logger().info("Aliasing objrefs {} and {}".format(objrefs[i].val, outputs[i].val))
worker.alias_objrefs(objrefs[i], outputs[i])
for i in range(len(objectids)):
if isinstance(outputs[i], ray.ObjectID):
# An ObjectID is being returned, so we must alias objectids[i] so that it refers to the same object that outputs[i] refers to
_logger().info("Aliasing objectids {} and {}".format(objectids[i].id, outputs[i].id))
worker.alias_objectids(objectids[i], outputs[i])
pass
else:
worker.put_object(objrefs[i], outputs[i])
worker.put_object(objectids[i], outputs[i])