179 lines
5.8 KiB
Python
179 lines
5.8 KiB
Python
import pytest
|
|
|
|
import ray
|
|
from ray.air.execution.resources.fixed import FixedResourceManager
|
|
from ray.air.execution.resources.request import ResourceRequest
|
|
from ray.util.scheduling_strategies import PlacementGroupSchedulingStrategy
|
|
|
|
REQUEST_2_CPU = ResourceRequest([{"CPU": 2}])
|
|
REQUEST_4_CPU = ResourceRequest([{"CPU": 4}])
|
|
REQUEST_1_2_CPU = ResourceRequest([{"CPU": 1}, {"CPU": 2}])
|
|
REQUEST_0_2_CPU = ResourceRequest([{"CPU": 0}, {"CPU": 2}])
|
|
|
|
|
|
@pytest.fixture(scope="module")
|
|
def ray_start_4_cpus():
|
|
address_info = ray.init(num_cpus=4)
|
|
yield address_info
|
|
# The code after the yield will run as teardown code.
|
|
ray.shutdown()
|
|
|
|
|
|
def test_acquire_return_resources(ray_start_4_cpus):
|
|
manager = FixedResourceManager(total_resources={"CPU": 4})
|
|
|
|
assert not manager.has_resources_ready(REQUEST_2_CPU)
|
|
assert not manager.has_resources_ready(REQUEST_4_CPU)
|
|
|
|
manager.request_resources(REQUEST_2_CPU)
|
|
manager.request_resources(REQUEST_4_CPU)
|
|
|
|
assert manager.has_resources_ready(REQUEST_4_CPU)
|
|
|
|
ready_2 = manager.acquire_resources(REQUEST_2_CPU)
|
|
|
|
assert manager.has_resources_ready(REQUEST_2_CPU)
|
|
assert not manager.has_resources_ready(REQUEST_4_CPU)
|
|
|
|
manager.free_resources(ready_2)
|
|
|
|
assert manager.has_resources_ready(REQUEST_4_CPU)
|
|
|
|
|
|
def test_numerical_error(ray_start_4_cpus):
|
|
"""Make sure we don't run into numerical errors when using fractional resources.
|
|
|
|
Legacy test: test_trial_runner::TrialRunnerTest::testResourceNumericalError
|
|
"""
|
|
manager = FixedResourceManager(
|
|
total_resources={"CPU": 0.99, "GPU": 0.99, "a": 0.99}
|
|
)
|
|
resource_request = ResourceRequest([{"CPU": 0.33, "GPU": 0.33, "a": 0.33}])
|
|
|
|
for i in range(3):
|
|
manager.request_resources(resource_request)
|
|
assert manager.acquire_resources(
|
|
resource_request=resource_request
|
|
), manager._available_resources
|
|
|
|
assert manager._available_resources["CPU"] == 0
|
|
assert manager._available_resources["GPU"] == 0
|
|
assert manager._available_resources["a"] == 0
|
|
|
|
|
|
def test_bind_two_bundles(ray_start_4_cpus):
|
|
"""Test that binding two remote objects to a ready resource works.
|
|
|
|
- Request resources with 2 bundles (1 CPU and 2 CPUs)
|
|
- Bind two remote tasks to these bundles, execute
|
|
- Assert that resource allocation returns the correct resources: 1 CPU and 2 CPUs
|
|
"""
|
|
manager = FixedResourceManager()
|
|
manager.request_resources(REQUEST_1_2_CPU)
|
|
|
|
assert manager.has_resources_ready(REQUEST_1_2_CPU)
|
|
|
|
@ray.remote
|
|
def get_assigned_resources():
|
|
return ray.get_runtime_context().get_assigned_resources()
|
|
|
|
acq = manager.acquire_resources(REQUEST_1_2_CPU)
|
|
[av1] = acq.annotate_remote_entities([get_assigned_resources])
|
|
|
|
res1 = ray.get(av1.remote())
|
|
|
|
assert sum(v for k, v in res1.items() if k.startswith("CPU")) == 1
|
|
|
|
[av1, av2] = acq.annotate_remote_entities(
|
|
[get_assigned_resources, get_assigned_resources]
|
|
)
|
|
|
|
res1, res2 = ray.get([av1.remote(), av2.remote()])
|
|
assert sum(v for k, v in res1.items() if k.startswith("CPU")) == 1
|
|
assert sum(v for k, v in res2.items() if k.startswith("CPU")) == 2
|
|
|
|
|
|
def test_bind_empty_head_bundle(ray_start_4_cpus):
|
|
"""Test that binding two remote objects to a ready resource works with empty head.
|
|
|
|
- Request resources with 2 bundles (0 CPU and 2 CPUs)
|
|
- Bind two remote tasks to these bundles, execute
|
|
- Assert that resource allocation returns the correct resources: 0 CPU and 2 CPUs
|
|
"""
|
|
manager = FixedResourceManager()
|
|
assert REQUEST_0_2_CPU.head_bundle_is_empty
|
|
manager.request_resources(REQUEST_0_2_CPU)
|
|
ray.wait(manager.get_resource_futures(), num_returns=1)
|
|
|
|
assert manager.has_resources_ready(REQUEST_0_2_CPU)
|
|
|
|
@ray.remote
|
|
def get_assigned_resources():
|
|
return ray.get_runtime_context().get_assigned_resources()
|
|
|
|
acq = manager.acquire_resources(REQUEST_0_2_CPU)
|
|
[av1] = acq.annotate_remote_entities([get_assigned_resources])
|
|
|
|
res1 = ray.get(av1.remote())
|
|
|
|
assert sum(v for k, v in res1.items() if k.startswith("CPU")) == 0
|
|
|
|
[av1, av2] = acq.annotate_remote_entities(
|
|
[get_assigned_resources, get_assigned_resources]
|
|
)
|
|
|
|
res1, res2 = ray.get([av1.remote(), av2.remote()])
|
|
assert sum(v for k, v in res1.items() if k.startswith("CPU")) == 0
|
|
assert sum(v for k, v in res2.items() if k.startswith("CPU")) == 2
|
|
|
|
|
|
@pytest.mark.parametrize("strategy", ["STRICT_PACK", "PACK", "SPREAD", "STRICT_SPREAD"])
|
|
def test_strategy(ray_start_4_cpus, strategy):
|
|
"""The fixed resoure manager does not support STRICT placement strategies."""
|
|
manager = FixedResourceManager()
|
|
|
|
req = ResourceRequest([{"CPU": 2}], strategy=strategy)
|
|
|
|
if strategy.startswith("STRICT_"):
|
|
with pytest.raises(RuntimeError):
|
|
manager.request_resources(req)
|
|
else:
|
|
manager.request_resources(req)
|
|
|
|
|
|
@pytest.mark.parametrize("strategy", ["STRICT_PACK", "PACK", "SPREAD", "STRICT_SPREAD"])
|
|
def test_strategy_nested(ray_start_4_cpus, strategy):
|
|
"""The fixed resoure manager does not support STRICT_SPREAD within a PG."""
|
|
|
|
@ray.remote
|
|
def nested_test():
|
|
manager = FixedResourceManager()
|
|
|
|
req = ResourceRequest([{"CPU": 2}], strategy=strategy)
|
|
|
|
if strategy == "STRICT_SPREAD":
|
|
with pytest.raises(RuntimeError):
|
|
manager.request_resources(req)
|
|
else:
|
|
manager.request_resources(req)
|
|
|
|
pg = ray.util.placement_group([{"CPU": 2}])
|
|
ray.wait([pg.ready()])
|
|
|
|
try:
|
|
ray.get(
|
|
nested_test.options(
|
|
scheduling_strategy=PlacementGroupSchedulingStrategy(
|
|
placement_group=pg, placement_group_capture_child_tasks=True
|
|
)
|
|
).remote()
|
|
)
|
|
finally:
|
|
ray.util.remove_placement_group(pg)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
import sys
|
|
|
|
sys.exit(pytest.main(["-v", __file__]))
|