Files
2026-07-13 13:17:40 +08:00

146 lines
4.7 KiB
Python

import argparse
import os
import math
from time import sleep, perf_counter
import json
import ray
from dashboard_test import DashboardTestAtScale
def test_max_actors_launch(cpus_per_actor, total_actors):
@ray.remote(num_cpus=cpus_per_actor)
class Actor:
def foo(self):
pass
print("Start launch actors")
actors = [Actor.options(max_restarts=-1).remote() for _ in range(total_actors)]
return actors
def parse_script_args():
parser = argparse.ArgumentParser()
parser.add_argument("--cpus-per-actor", type=float, default=0.2)
parser.add_argument("--total-actors", nargs="+", type=int, required=True)
parser.add_argument("--no-report", default=False, action="store_true")
parser.add_argument("--no-wait", default=False, action="store_true")
return parser.parse_known_args()
def scale_cluster_up(num_cpus):
print(f"Start to scale up to {num_cpus} cpus")
def get_curr_cpus():
return int(sum([r.get("Resources", {}).get("CPU", 0) for r in ray.nodes()]))
step = 1000
curr_cpus = get_curr_cpus()
target_cpus = curr_cpus
while curr_cpus < num_cpus:
curr_cpus = get_curr_cpus()
new_target_cpus = min(curr_cpus + step, num_cpus)
if new_target_cpus != target_cpus:
target_cpus = new_target_cpus
ray.autoscaler.sdk.request_resources(num_cpus=target_cpus)
print(f"Waiting for cluster to be up: {curr_cpus}->{target_cpus}->{num_cpus}")
sleep(10)
def get_head_node_cpus():
head_ip = ray.util.get_node_ip_address()
for node in ray.nodes():
if node["Alive"] and node["NodeManagerAddress"] == head_ip:
return int(node.get("Resources", {}).get("CPU", 0))
return 0
def run_one(total_actors, cpus_per_actor, no_wait):
total_cpus = cpus_per_actor * total_actors + get_head_node_cpus()
total_cpus = int(math.ceil(total_cpus))
scale_cluster_up(total_cpus)
actor_launch_start = perf_counter()
actors = test_max_actors_launch(cpus_per_actor, total_actors)
actor_launch_end = perf_counter()
actor_launch_time = actor_launch_end - actor_launch_start
actor_ready_start = perf_counter()
total_actors = len(actors)
objs = [actor.foo.remote() for actor in actors]
while len(objs) != 0:
timeout = None if no_wait else 30
objs_ready, objs = ray.wait(objs, num_returns=len(objs), timeout=timeout)
print(
f"Status: {total_actors - len(objs)}/{total_actors}, "
f"{perf_counter() - actor_ready_start}"
)
actor_ready_end = perf_counter()
actor_ready_time = actor_ready_end - actor_ready_start
throughput = total_actors / (actor_ready_time + actor_launch_time)
print(f"Actor launch time: {actor_launch_time} ({total_actors} actors)")
print(f"Actor ready time: {actor_ready_time} ({total_actors} actors)")
print(
f"Total time: {actor_launch_time + actor_ready_time}"
f" ({total_actors} actors)"
)
print(f"Through put: {throughput}")
return {
"actor_launch_time": actor_launch_time,
"actor_ready_time": actor_ready_time,
"total_time": actor_launch_time + actor_ready_time,
"num_actors": total_actors,
"throughput": throughput,
}
def main():
args, unknown = parse_script_args()
args.total_actors.sort()
addr = ray.init(address="auto")
dashboard_test = DashboardTestAtScale(addr)
result = {}
for i in args.total_actors:
result[f"many_nodes_actor_tests_{i}"] = run_one(
i, args.cpus_per_actor, args.no_wait
)
# Print the results early so if failed in the future, we still
# can see it in the log.
print(f"Result: {json.dumps(result, indent=2)}")
if "TEST_OUTPUT_JSON" in os.environ and not args.no_report:
with open(os.environ["TEST_OUTPUT_JSON"], "w") as out_file:
perf = [
{
"perf_metric_name": name,
"perf_metric_value": r["throughput"],
"perf_metric_type": "THROUGHPUT",
}
for (name, r) in result.items()
]
result["perf_metrics"] = perf
dashboard_test.update_release_test_result(result)
print(f"Writing data into file: {os.environ['TEST_OUTPUT_JSON']}")
json.dump(result, out_file)
print("Test finished successfully!")
ray.shutdown()
# We need to make sure GCS cool down otherwise, testing infra
# might get timeout when fetching the result because when the driver
# got shutdown, many actors needs to be terminated which will
# overload GCS.
print("Sleep for 60s, waiting for the cluster to cool down.")
sleep(60)
if __name__ == "__main__":
main()