import os import sys from unittest.mock import patch import pytest import ray from ray._private.accelerators import HPUAcceleratorManager, hpu from ray.util.placement_group import placement_group from ray.util.scheduling_strategies import PlacementGroupSchedulingStrategy def test_user_configured_more_than_visible(monkeypatch, call_ray_stop_only): # Test more hpus are configured than visible. monkeypatch.setenv("HABANA_VISIBLE_MODULES", "0,1,2") with pytest.raises(ValueError): ray.init(resources={"HPU": 4}) @patch( "ray._private.accelerators.HPUAcceleratorManager.get_current_node_num_accelerators", # noqa: E501 return_value=4, ) def test_auto_detected_more_than_visible( mock_get_num_accelerators, monkeypatch, shutdown_only ): # Test more hpus are detected than visible. monkeypatch.setenv("HABANA_VISIBLE_MODULES", "0,1,2") ray.init() _ = mock_get_num_accelerators.called assert ray.available_resources()["HPU"] == 3 @patch( "ray._private.accelerators.HPUAcceleratorManager.get_current_node_num_accelerators", # noqa: E501 return_value=2, ) def test_auto_detect_resources(mock_get_num_accelerators, shutdown_only): # Test that ray node resources are filled with auto detected count. ray.init() _ = mock_get_num_accelerators.called assert ray.available_resources()["HPU"] == 2 def test_get_current_process_visible_accelerator_ids(): os.environ[hpu.HABANA_VISIBLE_DEVICES_ENV_VAR] = "0,1,2" assert HPUAcceleratorManager.get_current_process_visible_accelerator_ids() == [ "0", "1", "2", ] # noqa: E501 del os.environ[hpu.HABANA_VISIBLE_DEVICES_ENV_VAR] assert HPUAcceleratorManager.get_current_process_visible_accelerator_ids() is None os.environ[hpu.HABANA_VISIBLE_DEVICES_ENV_VAR] = "" assert HPUAcceleratorManager.get_current_process_visible_accelerator_ids() == [] del os.environ[hpu.HABANA_VISIBLE_DEVICES_ENV_VAR] def test_set_current_process_visible_accelerator_ids(): HPUAcceleratorManager.set_current_process_visible_accelerator_ids(["0"]) assert os.environ[hpu.HABANA_VISIBLE_DEVICES_ENV_VAR] == "0" HPUAcceleratorManager.set_current_process_visible_accelerator_ids(["0", "1"]) assert os.environ[hpu.HABANA_VISIBLE_DEVICES_ENV_VAR] == "0,1" HPUAcceleratorManager.set_current_process_visible_accelerator_ids(["0", "1", "2"]) assert os.environ[hpu.HABANA_VISIBLE_DEVICES_ENV_VAR] == "0,1,2" del os.environ[hpu.HABANA_VISIBLE_DEVICES_ENV_VAR] @pytest.mark.parametrize( "test_config", [ (1, False), (0.5, True), (3, False), ], ) def test_validate_resource_request_quantity(test_config): num_hpus, expect_error = test_config if expect_error: assert ( HPUAcceleratorManager.validate_resource_request_quantity(num_hpus)[0] is False ) assert ( HPUAcceleratorManager.validate_resource_request_quantity(num_hpus)[1] is not None ) else: assert ( HPUAcceleratorManager.validate_resource_request_quantity(num_hpus)[0] is True ) assert ( HPUAcceleratorManager.validate_resource_request_quantity(num_hpus)[1] is None ) def test_check_accelerator_info(): if HPUAcceleratorManager.is_initialized(): assert ( "Intel-GAUDI" in HPUAcceleratorManager.get_current_node_accelerator_type() ) else: assert HPUAcceleratorManager.get_current_node_accelerator_type() is None assert HPUAcceleratorManager.get_resource_name() == "HPU" def test_decorator_args(): # This is a valid way of using the decorator. @ray.remote(resources={"HPU": 1}) # noqa: F811 class Actor: # noqa: F811 def __init__(self): pass # This is a valid way of using the decorator. @ray.remote(num_cpus=1, resources={"HPU": 1}) # noqa: F811 class Actor: # noqa: F811 def __init__(self): pass def test_actor_deletion_with_hpus(shutdown_only): ray.init(num_cpus=1, resources={"HPU": 1}) # When an actor that uses an HPU exits, make sure that the HPU resources # are released. @ray.remote(resources={"HPU": 1}) class Actor: def getpid(self): return os.getpid() for _ in range(5): # If we can successfully create an actor, that means that enough # HPU resources are available. a = Actor.remote() ray.get(a.getpid.remote()) @pytest.mark.skipif(sys.platform == "win32", reason="Failing on Windows.") def test_actor_hpus(ray_start_cluster): cluster = ray_start_cluster num_nodes = 2 num_hpus_per_raylet = 2 for i in range(num_nodes): cluster.add_node(num_cpus=10 * 2, resources={"HPU": num_hpus_per_raylet}) ray.init(address=cluster.address) @ray.remote(resources={"HPU": 1}) class Actor1: def __init__(self): resource_ids = ray.get_runtime_context().get_accelerator_ids() self.hpu_ids = resource_ids.get("HPU") def get_location_and_ids(self): return ( ray.get_runtime_context().get_node_id(), tuple(self.hpu_ids), ) # Create one actor per HPU. actors = [Actor1.remote() for _ in range(num_nodes * num_hpus_per_raylet)] # Make sure that no two actors are assigned to the same HPU. locations_and_ids = ray.get( [actor.get_location_and_ids.remote() for actor in actors] ) node_names = {location for location, hpu_id in locations_and_ids} assert len(node_names) == num_nodes location_actor_combinations = [] for node_name in node_names: for hpu_id in range(num_hpus_per_raylet): location_actor_combinations.append((node_name, (f"{hpu_id}",))) assert set(locations_and_ids) == set(location_actor_combinations) # Creating a new actor should fail because all of the HPUs are being # used. a = Actor1.remote() ready_ids, _ = ray.wait([a.get_location_and_ids.remote()], timeout=0.01) assert ready_ids == [] def test_actor_habana_visible_devices(shutdown_only): """Test user can overwrite HABANA_VISIBLE_MODULES after the actor is created.""" ray.init(resources={"HPU": 1}) @ray.remote(resources={"HPU": 1}) class Actor: def set_habana_visible_devices(self, habana_visible_devices): os.environ["HABANA_VISIBLE_MODULES"] = habana_visible_devices def get_habana_visible_devices(self): return os.environ["HABANA_VISIBLE_MODULES"] actor = Actor.remote() assert ray.get(actor.get_habana_visible_devices.remote()) == "0" ray.get(actor.set_habana_visible_devices.remote("0,1")) assert ray.get(actor.get_habana_visible_devices.remote()) == "0,1" def test_hpu_ids(shutdown_only): num_hpus = 3 ray.init(num_cpus=num_hpus, resources={"HPU": num_hpus}) def get_hpu_ids(hpus_per_worker): hpu_ids = ray.get_runtime_context().get_accelerator_ids()["HPU"] assert len(hpu_ids) == hpus_per_worker modules = os.environ.get("HABANA_VISIBLE_MODULES") if modules is not None: assert modules == ",".join([str(i) for i in hpu_ids]) # noqa for hpu_id in hpu_ids: assert hpu_id in [str(i) for i in range(num_hpus)] return hpu_ids f0 = ray.remote(resources={"HPU": 0})(lambda: get_hpu_ids(0)) f1 = ray.remote(resources={"HPU": 1})(lambda: get_hpu_ids(1)) list_of_ids = ray.get([f0.remote() for _ in range(10)]) assert list_of_ids == 10 * [[]] ray.get([f1.remote() for _ in range(10)]) # Test that actors have HABANA_VISIBLE_MODULES set properly. def _check_hpu_env(expected_num_hpus): hpu_ids = ray.get_runtime_context().get_accelerator_ids()["HPU"] assert len(hpu_ids) == expected_num_hpus if expected_num_hpus > 0: assert os.environ["HABANA_VISIBLE_MODULES"] == ",".join( [str(i) for i in hpu_ids] # noqa ) else: assert os.environ.get("HABANA_VISIBLE_MODULES") is None @ray.remote class Actor: def __init__(self, num_hpus): self.num_hpus = num_hpus _check_hpu_env(num_hpus) self.x = num_hpus def test(self): _check_hpu_env(self.num_hpus) return self.x a0 = Actor.remote(0) assert ray.get(a0.test.remote()) == 0 a1 = Actor.options(resources={"HPU": 1}).remote(1) assert ray.get(a1.test.remote()) == 1 def test_hpu_with_placement_group(shutdown_only): num_hpus = 2 ray.init(num_cpus=1, resources={"HPU": num_hpus}) @ray.remote(resources={"HPU": num_hpus}) class HPUActor: def __init__(self): pass def ready(self): hpu_ids = ray.get_runtime_context().get_accelerator_ids()["HPU"] assert len(hpu_ids) == num_hpus assert os.environ["HABANA_VISIBLE_MODULES"] == ",".join( [str(i) for i in hpu_ids] # noqa ) # Reserve a placement group of 1 bundle that reserves 1 CPU and 2 HPU. pg = placement_group([{"CPU": 1, "HPU": num_hpus}]) # Wait until placement group is created. ray.get(pg.ready(), timeout=10) actor = HPUActor.options( scheduling_strategy=PlacementGroupSchedulingStrategy( placement_group=pg, ) ).remote() ray.get(actor.ready.remote(), timeout=10) if __name__ == "__main__": sys.exit(pytest.main(["-sv", __file__]))