def placement_group(bundles: List[Dict[str, float]], strategy: str = "PACK", name: str = "", lifetime=None) -> PlacementGroup: """Asynchronously creates a PlacementGroup. Args: bundles(List[Dict]): A list of bundles which represent the resources requirements. strategy(str): The strategy to create the placement group. - "PACK": Packs Bundles into as few nodes as possible. - "SPREAD": Places Bundles across distinct nodes as even as possible. - "STRICT_PACK": Packs Bundles into one node. The group is not allowed to span multiple nodes. - "STRICT_SPREAD": Packs Bundles across distinct nodes. name(str): The name of the placement group. lifetime(str): Either `None`, which defaults to the placement group will fate share with its creator and will be deleted once its creator is dead, or "detached", which means the placement group will live as a global object independent of the creator. Return: PlacementGroup: Placement group object. """ worker = ray.worker.global_worker worker.check_connected() if not isinstance(bundles, list): raise ValueError( "The type of bundles must be list, got {}".format(bundles)) # Validate bundles for bundle in bundles: if (len(bundle) == 0 or all(resource_value == 0 for resource_value in bundle.values())): raise ValueError( "Bundles cannot be an empty dictionary or " f"resources with only 0 values. Bundles: {bundles}") if "memory" in bundle.keys() and bundle["memory"] > 0: # Make sure the memory resource can be # transformed to memory unit. to_memory_units(bundle["memory"], True) if lifetime is None: detached = False elif lifetime == "detached": detached = True else: raise ValueError("placement group `lifetime` argument must be either" " `None` or 'detached'") placement_group_id = worker.core_worker.create_placement_group( name, bundles, strategy, detached) return PlacementGroup(placement_group_id)
def to_resource_dict(self): """Returns a dict suitable to pass to raylet initialization. This renames num_cpus / num_gpus to "CPU" / "GPU", translates memory from bytes into 100MB memory units, and checks types. """ assert self.resolved() memory_units = ray_constants.to_memory_units(self.memory, round_up=False) reservable_object_store_memory = ( self.object_store_memory * ray_constants.PLASMA_RESERVABLE_MEMORY_FRACTION) if (reservable_object_store_memory < ray_constants.MEMORY_RESOURCE_UNIT_BYTES): raise ValueError( "The minimum amount of object_store_memory that can be " "requested is {}, but you specified {}.".format( int( math.ceil( ray_constants.MEMORY_RESOURCE_UNIT_BYTES / ray_constants.PLASMA_RESERVABLE_MEMORY_FRACTION)), self.object_store_memory)) object_store_memory_units = ray_constants.to_memory_units( self.object_store_memory * ray_constants.PLASMA_RESERVABLE_MEMORY_FRACTION, round_up=False) resources = dict(self.resources, CPU=self.num_cpus, GPU=self.num_gpus, memory=memory_units, object_store_memory=object_store_memory_units) resources = { resource_label: resource_quantity for resource_label, resource_quantity in resources.items() if resource_quantity != 0 } # Check types. for _, resource_quantity in resources.items(): assert (isinstance(resource_quantity, int) or isinstance(resource_quantity, float)) if (isinstance(resource_quantity, float) and not resource_quantity.is_integer()): raise ValueError( "Resource quantities must all be whole numbers. " "Received {}.".format(resources)) if resource_quantity < 0: raise ValueError("Resource quantities must be nonnegative. " "Received {}.".format(resources)) if resource_quantity > ray_constants.MAX_RESOURCE_QUANTITY: raise ValueError( "Resource quantities must be at most {}.".format( ray_constants.MAX_RESOURCE_QUANTITY)) return resources
def to_resource_dict(self): """Returns a dict suitable to pass to raylet initialization. This renames num_cpus / num_gpus to "CPU" / "GPU", translates memory from bytes into 100MB memory units, and checks types. """ assert self.resolved() memory_units = ray_constants.to_memory_units(self.memory, round_up=False) object_store_memory_units = ray_constants.to_memory_units( self.object_store_memory, round_up=False) resources = dict( self.resources, CPU=self.num_cpus, GPU=self.num_gpus, memory=memory_units, object_store_memory=object_store_memory_units, ) resources = { resource_label: resource_quantity for resource_label, resource_quantity in resources.items() if resource_quantity != 0 } # Check types. for resource_label, resource_quantity in resources.items(): assert isinstance(resource_quantity, int) or isinstance( resource_quantity, float), (f"{resource_label} ({type(resource_quantity)}): " f"{resource_quantity}") if (isinstance(resource_quantity, float) and not resource_quantity.is_integer()): raise ValueError( "Resource quantities must all be whole numbers. " "Violated by resource '{}' in {}.".format( resource_label, resources)) if resource_quantity < 0: raise ValueError("Resource quantities must be nonnegative. " "Violated by resource '{}' in {}.".format( resource_label, resources)) if resource_quantity > ray_constants.MAX_RESOURCE_QUANTITY: raise ValueError("Resource quantities must be at most {}. " "Violated by resource '{}' in {}.".format( ray_constants.MAX_RESOURCE_QUANTITY, resource_label, resources)) return resources
def _convert_memory_unit(node_types: Dict[NodeType, NodeTypeConfigDict] ) -> Dict[NodeType, NodeTypeConfigDict]: """Convert memory and object_store_memory to memory unit""" node_types = copy.deepcopy(node_types) for node_type in node_types: res = node_types[node_type].get("resources", {}) if "memory" in res: size = float(res["memory"]) res["memory"] = ray_constants.to_memory_units(size, False) if "object_store_memory" in res: size = float(res["object_store_memory"]) res["object_store_memory"] = ray_constants.to_memory_units( size, False) if res: node_types[node_type]["resources"] = res return node_types
def resources_from_ray_options(options_dict: Dict[str, Any]) -> Dict[str, Any]: """Determine a task's resource requirements. Args: options_dict: The dictionary that contains resources requirements. Returns: A dictionary of the resource requirements for the task. """ resources = (options_dict.get("resources") or {}).copy() if "CPU" in resources or "GPU" in resources: raise ValueError( "The resources dictionary must not contain the key 'CPU' or 'GPU'") elif "memory" in resources or "object_store_memory" in resources: raise ValueError("The resources dictionary must not " "contain the key 'memory' or 'object_store_memory'") num_cpus = options_dict.get("num_cpus") num_gpus = options_dict.get("num_gpus") memory = options_dict.get("memory") object_store_memory = options_dict.get("object_store_memory") accelerator_type = options_dict.get("accelerator_type") if num_cpus is not None: resources["CPU"] = num_cpus if num_gpus is not None: resources["GPU"] = num_gpus if memory is not None: resources["memory"] = ray_constants.to_memory_units(memory, round_up=True) if object_store_memory is not None: resources["object_store_memory"] = ray_constants.to_memory_units( object_store_memory, round_up=True) if accelerator_type is not None: resources[ f"{ray_constants.RESOURCE_CONSTRAINT_PREFIX}{accelerator_type}"] = 0.001 return resources
def resources_from_resource_arguments( default_num_cpus, default_num_gpus, default_memory, default_object_store_memory, default_resources, default_accelerator_type, runtime_num_cpus, runtime_num_gpus, runtime_memory, runtime_object_store_memory, runtime_resources, runtime_accelerator_type): """Determine a task's resource requirements. Args: default_num_cpus: The default number of CPUs required by this function or actor method. default_num_gpus: The default number of GPUs required by this function or actor method. default_memory: The default heap memory required by this function or actor method. default_object_store_memory: The default object store memory required by this function or actor method. default_resources: The default custom resources required by this function or actor method. runtime_num_cpus: The number of CPUs requested when the task was invoked. runtime_num_gpus: The number of GPUs requested when the task was invoked. runtime_memory: The heap memory requested when the task was invoked. runtime_object_store_memory: The object store memory requested when the task was invoked. runtime_resources: The custom resources requested when the task was invoked. Returns: A dictionary of the resource requirements for the task. """ if runtime_resources is not None: resources = runtime_resources.copy() elif default_resources is not None: resources = default_resources.copy() else: resources = {} if "CPU" in resources or "GPU" in resources: raise ValueError("The resources dictionary must not " "contain the key 'CPU' or 'GPU'") elif "memory" in resources or "object_store_memory" in resources: raise ValueError("The resources dictionary must not " "contain the key 'memory' or 'object_store_memory'") assert default_num_cpus is not None resources["CPU"] = (default_num_cpus if runtime_num_cpus is None else runtime_num_cpus) if runtime_num_gpus is not None: resources["GPU"] = runtime_num_gpus elif default_num_gpus is not None: resources["GPU"] = default_num_gpus # Order of arguments matter for short circuiting. memory = runtime_memory or default_memory object_store_memory = (runtime_object_store_memory or default_object_store_memory) if memory is not None: resources["memory"] = ray_constants.to_memory_units(memory, round_up=True) if object_store_memory is not None: resources["object_store_memory"] = ray_constants.to_memory_units( object_store_memory, round_up=True) if runtime_accelerator_type is not None: resources[f"{ray_constants.RESOURCE_CONSTRAINT_PREFIX}" f"{runtime_accelerator_type}"] = 0.001 elif default_accelerator_type is not None: resources[f"{ray_constants.RESOURCE_CONSTRAINT_PREFIX}" f"{default_accelerator_type}"] = 0.001 return resources
def resources_from_resource_arguments( default_num_cpus, default_num_gpus, default_memory, default_object_store_memory, default_resources, runtime_num_cpus, runtime_num_gpus, runtime_memory, runtime_object_store_memory, runtime_resources): """Determine a task's resource requirements. Args: default_num_cpus: The default number of CPUs required by this function or actor method. default_num_gpus: The default number of GPUs required by this function or actor method. default_memory: The default heap memory required by this function or actor method. default_object_store_memory: The default object store memory required by this function or actor method. default_resources: The default custom resources required by this function or actor method. runtime_num_cpus: The number of CPUs requested when the task was invoked. runtime_num_gpus: The number of GPUs requested when the task was invoked. runtime_memory: The heap memory requested when the task was invoked. runtime_object_store_memory: The object store memory requested when the task was invoked. runtime_resources: The custom resources requested when the task was invoked. Returns: A dictionary of the resource requirements for the task. """ if runtime_resources is not None: resources = runtime_resources.copy() elif default_resources is not None: resources = default_resources.copy() else: resources = {} if "CPU" in resources or "GPU" in resources: raise ValueError("The resources dictionary must not " "contain the key 'CPU' or 'GPU'") elif "memory" in resources or "object_store_memory" in resources: raise ValueError("The resources dictionary must not " "contain the key 'memory' or 'object_store_memory'") assert default_num_cpus is not None resources["CPU"] = (default_num_cpus if runtime_num_cpus is None else runtime_num_cpus) if runtime_num_gpus is not None: resources["GPU"] = runtime_num_gpus elif default_num_gpus is not None: resources["GPU"] = default_num_gpus memory = default_memory or runtime_memory object_store_memory = (default_object_store_memory or runtime_object_store_memory) if memory is not None: resources["memory"] = ray_constants.to_memory_units(memory, round_up=True) if object_store_memory is not None: resources["object_store_memory"] = ray_constants.to_memory_units( object_store_memory, round_up=True) return resources