Exemple #1
0
def compute_actor_handle_id_non_forked(actor_handle_id, current_task_id):
    """Deterministically compute an actor handle ID in the non-forked case.

    This code path is used whenever an actor handle is pickled and unpickled
    (for example, if a remote function closes over an actor handle). Then,
    whenever the actor handle is used, a new actor handle ID will be generated
    on the fly as a deterministic function of the actor ID, the previous actor
    handle ID and the current task ID.

    TODO(rkn): It may be possible to cause problems by closing over multiple
    actor handles in a remote function, which then get unpickled and give rise
    to the same actor handle IDs.

    Args:
        actor_handle_id: The original actor handle ID.
        current_task_id: The ID of the task that is unpickling the handle.

    Returns:
        An ID for the new actor handle.
    """
    assert isinstance(actor_handle_id, ActorHandleID)
    assert isinstance(current_task_id, TaskID)
    handle_id_hash = hashlib.sha1()
    handle_id_hash.update(actor_handle_id.binary())
    handle_id_hash.update(current_task_id.binary())
    handle_id = handle_id_hash.digest()
    return ActorHandleID(handle_id)
Exemple #2
0
    def _serialization_helper(self, ray_forking):
        """This is defined in order to make pickling work.

        Args:
            ray_forking: True if this is being called because Ray is forking
                the actor handle and false if it is being called by pickling.

        Returns:
            A dictionary of the information needed to reconstruct the object.
        """
        if ray_forking:
            actor_handle_id = compute_actor_handle_id(
                self._ray_actor_handle_id, self._ray_actor_forks)
        else:
            actor_handle_id = self._ray_actor_handle_id

        # Note: _ray_actor_cursor and _ray_actor_creation_dummy_object_id
        # could be None.
        state = {
            "actor_id": self._ray_actor_id,
            "actor_handle_id": actor_handle_id,
            "module_name": self._ray_module_name,
            "class_name": self._ray_class_name,
            "actor_cursor": self._ray_actor_cursor,
            "actor_method_names": self._ray_actor_method_names,
            "method_decorators": self._ray_method_decorators,
            "method_signatures": self._ray_method_signatures,
            "method_num_return_vals": self._ray_method_num_return_vals,
            # Actors in local mode don't have dummy objects.
            "actor_creation_dummy_object_id": self.
            _ray_actor_creation_dummy_object_id,
            "actor_method_cpus": self._ray_actor_method_cpus,
            "actor_job_id": self._ray_actor_job_id,
            "ray_forking": ray_forking
        }

        if ray_forking:
            self._ray_actor_forks += 1
            new_actor_handle_id = actor_handle_id
        else:
            # The execution dependency for a pickled actor handle is never safe
            # to release, since it could be unpickled and submit another
            # dependent task at any time. Therefore, we notify the backend of a
            # random handle ID that will never actually be used.
            new_actor_handle_id = ActorHandleID.from_random()
        # Notify the backend to expect this new actor handle. The backend will
        # not release the cursor for any new handles until the first task for
        # each of the new handles is submitted.
        # NOTE(swang): There is currently no garbage collection for actor
        # handles until the actor itself is removed.
        self._ray_new_actor_handles.append(new_actor_handle_id)

        return state
Exemple #3
0
def compute_actor_handle_id(actor_handle_id, num_forks):
    """Deterministically compute an actor handle ID.

    A new actor handle ID is generated when it is forked from another actor
    handle. The new handle ID is computed as hash(old_handle_id || num_forks).

    Args:
        actor_handle_id (common.ObjectID): The original actor handle ID.
        num_forks: The number of times the original actor handle has been
                   forked so far.

    Returns:
        An ID for the new actor handle.
    """
    assert isinstance(actor_handle_id, ActorHandleID)
    handle_id_hash = hashlib.sha1()
    handle_id_hash.update(actor_handle_id.binary())
    handle_id_hash.update(str(num_forks).encode("ascii"))
    handle_id = handle_id_hash.digest()
    return ActorHandleID(handle_id)
Exemple #4
0
 def __init__(self,
              actor_id,
              module_name,
              class_name,
              actor_cursor,
              actor_method_names,
              method_decorators,
              method_signatures,
              method_num_return_vals,
              actor_creation_dummy_object_id,
              actor_method_cpus,
              actor_job_id,
              session_and_job,
              actor_handle_id=None):
     assert isinstance(actor_id, ActorID)
     assert isinstance(actor_job_id, ray.JobID)
     self._ray_actor_id = actor_id
     self._ray_module_name = module_name
     # False if this actor handle was created by forking or pickling. True
     # if it was created by the _serialization_helper function.
     self._ray_original_handle = actor_handle_id is None
     if self._ray_original_handle:
         self._ray_actor_handle_id = ActorHandleID.nil()
     else:
         assert isinstance(actor_handle_id, ActorHandleID)
         self._ray_actor_handle_id = actor_handle_id
     self._ray_actor_cursor = actor_cursor
     self._ray_actor_counter = 0
     self._ray_actor_method_names = actor_method_names
     self._ray_method_decorators = method_decorators
     self._ray_method_signatures = method_signatures
     self._ray_method_num_return_vals = method_num_return_vals
     self._ray_class_name = class_name
     self._ray_actor_forks = 0
     self._ray_actor_creation_dummy_object_id = (
         actor_creation_dummy_object_id)
     self._ray_actor_method_cpus = actor_method_cpus
     self._ray_actor_job_id = actor_job_id
     self._ray_session_and_job = session_and_job
     self._ray_new_actor_handles = []
     self._ray_actor_lock = threading.Lock()
Exemple #5
0
 def __init__(self,
              actor_id,
              module_name,
              class_name,
              actor_cursor,
              actor_method_names,
              method_signatures,
              method_num_return_vals,
              actor_creation_dummy_object_id,
              actor_method_cpus,
              actor_driver_id,
              actor_handle_id=None):
     assert isinstance(actor_id, ActorID)
     assert isinstance(actor_driver_id, DriverID)
     self._ray_actor_id = actor_id
     self._ray_module_name = module_name
     # False if this actor handle was created by forking or pickling. True
     # if it was created by the _serialization_helper function.
     self._ray_original_handle = actor_handle_id is None
     if self._ray_original_handle:
         self._ray_actor_handle_id = ActorHandleID.nil()
     else:
         assert isinstance(actor_handle_id, ActorHandleID)
         self._ray_actor_handle_id = actor_handle_id
     self._ray_actor_cursor = actor_cursor
     self._ray_actor_counter = 0
     self._ray_actor_method_names = actor_method_names
     self._ray_method_signatures = method_signatures
     self._ray_method_num_return_vals = method_num_return_vals
     self._ray_class_name = class_name
     self._ray_actor_forks = 0
     self._ray_actor_creation_dummy_object_id = (
         actor_creation_dummy_object_id)
     self._ray_actor_method_cpus = actor_method_cpus
     self._ray_actor_driver_id = actor_driver_id
     self._ray_new_actor_handles = []
     self._ray_actor_lock = threading.Lock()
Exemple #6
0
    def _remote(self,
                args=None,
                kwargs=None,
                num_cpus=None,
                num_gpus=None,
                memory=None,
                object_store_memory=None,
                resources=None):
        """Create an actor.

        This method allows more flexibility than the remote method because
        resource requirements can be specified and override the defaults in the
        decorator.

        Args:
            args: The arguments to forward to the actor constructor.
            kwargs: The keyword arguments to forward to the actor constructor.
            num_cpus: The number of CPUs required by the actor creation task.
            num_gpus: The number of GPUs required by the actor creation task.
            memory: Restrict the heap memory usage of this actor.
            object_store_memory: Restrict the object store memory used by
                this actor when creating objects.
            resources: The custom resources required by the actor creation
                task.

        Returns:
            A handle to the newly created actor.
        """
        if args is None:
            args = []
        if kwargs is None:
            kwargs = {}

        worker = ray.worker.get_global_worker()
        if worker.mode is None:
            raise Exception("Actors cannot be created before ray.init() "
                            "has been called.")

        meta = self.__ray_metadata__

        # Set the actor's default resources if not already set. First three
        # conditions are to check that no resources were specified in the
        # decorator. Last three conditions are to check that no resources were
        # specified when _remote() was called.
        if (meta.num_cpus is None and meta.num_gpus is None
                and meta.resources is None and num_cpus is None
                and num_gpus is None and resources is None):
            # In the default case, actors acquire no resources for
            # their lifetime, and actor methods will require 1 CPU.
            cpus_to_use = ray_constants.DEFAULT_ACTOR_CREATION_CPU_SIMPLE
            actor_method_cpu = ray_constants.DEFAULT_ACTOR_METHOD_CPU_SIMPLE
        else:
            # If any resources are specified (here or in decorator), then
            # all resources are acquired for the actor's lifetime and no
            # resources are associated with methods.
            cpus_to_use = (ray_constants.DEFAULT_ACTOR_CREATION_CPU_SPECIFIED
                           if meta.num_cpus is None else meta.num_cpus)
            actor_method_cpu = ray_constants.DEFAULT_ACTOR_METHOD_CPU_SPECIFIED

        function_name = "__init__"
        function_descriptor = FunctionDescriptor(
            meta.modified_class.__module__, function_name,
            meta.modified_class.__name__)

        # Do not export the actor class or the actor if run in LOCAL_MODE
        # Instead, instantiate the actor locally and add it to the worker's
        # dictionary
        if worker.mode == ray.LOCAL_MODE:
            actor_id = ActorID.of(worker.current_job_id,
                                  worker.current_task_id,
                                  worker.task_context.task_index + 1)
            worker.actors[actor_id] = meta.modified_class(
                *copy.deepcopy(args), **copy.deepcopy(kwargs))
            core_handle = ray._raylet.ActorHandle(
                actor_id, ActorHandleID.nil(), worker.current_job_id,
                function_descriptor.get_function_descriptor_list())
        else:
            # Export the actor.
            if (meta.last_export_session_and_job !=
                    worker.current_session_and_job):
                # If this actor class was not exported in this session and job,
                # we need to export this function again, because current GCS
                # doesn't have it.
                meta.last_export_session_and_job = (
                    worker.current_session_and_job)
                worker.function_actor_manager.export_actor_class(
                    meta.modified_class, meta.actor_method_names)

            resources = ray.utils.resources_from_resource_arguments(
                cpus_to_use, meta.num_gpus, meta.memory,
                meta.object_store_memory, meta.resources, num_cpus, num_gpus,
                memory, object_store_memory, resources)

            # If the actor methods require CPU resources, then set the required
            # placement resources. If actor_placement_resources is empty, then
            # the required placement resources will be the same as resources.
            actor_placement_resources = {}
            assert actor_method_cpu in [0, 1]
            if actor_method_cpu == 1:
                actor_placement_resources = resources.copy()
                actor_placement_resources["CPU"] += 1

            function_signature = meta.method_signatures[function_name]
            creation_args = signature.extend_args(function_signature, args,
                                                  kwargs)
            core_handle = worker.core_worker.create_actor(
                function_descriptor.get_function_descriptor_list(),
                creation_args, meta.max_reconstructions, resources,
                actor_placement_resources)

        actor_handle = ActorHandle(core_handle,
                                   meta.modified_class.__module__,
                                   meta.class_name,
                                   meta.actor_method_names,
                                   meta.method_decorators,
                                   meta.method_signatures,
                                   meta.actor_method_num_return_vals,
                                   actor_method_cpu,
                                   worker.current_session_and_job,
                                   original_handle=True)

        return actor_handle