Files
wehub-resource-sync a203934033
Lint test / lint (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:34:58 +08:00

138 lines
5.4 KiB
Python

# Copyright (c) ModelScope Contributors. All rights reserved.
# Some code borrowed from ROLL: https://github.com/alibaba/ROLL
import math
import os
from dataclasses import dataclass, field
from typing import Any, Dict, List
@dataclass
class NodeGroup:
device_count: int
nodes: List[Any] = field(default_factory=list)
def get_node_rank():
return int(os.environ.get('NODE_RANK', '0'))
class ResourceManager:
possible_keys = ['nproc_per_node', 'nnodes']
def __init__(self, groups: Dict[str, Any]):
import ray
from ray.util.placement_group import PlacementGroup
nproc_per_node = int(groups['nproc_per_node'])
device_types = set([group['device'].upper()
for group in groups.values() if hasattr(group, '__getitem__')]) - {'CPU'}
assert len(device_types) == 1
device_type = next(iter(device_types))
all_ranks = []
last_rank = -1
cpu_proc_count = 0
for group_name, group in groups.items():
if group_name in self.possible_keys:
continue
ranks = group['ranks']
device = group['device'].upper()
if device == 'CPU':
assert isinstance(ranks, int), 'CPU group only supports integer ranks'
cpu_proc_count += ranks
continue
try:
ranks = int(ranks) # int type
ranks = list(range(last_rank + 1, last_rank + 1 + ranks))
except Exception: # noqa
if isinstance(ranks, str):
ranks = eval(ranks, {'__builtins__': {'list': list, 'range': range}})
finally:
all_ranks.extend(ranks)
group['ranks'] = ranks
last_rank = ranks[-1]
assert len(set(all_ranks)) == len(all_ranks)
groups['nnodes'] = math.ceil(len(all_ranks) / nproc_per_node)
self.nodes = []
for node in ray.nodes():
resource = node['Resources']
node_gpu_num = int(resource.get(device_type, 0))
if node_gpu_num >= nproc_per_node:
self.nodes.append(node)
bundles = []
cpu_bundles = []
for i in range(groups['nnodes']):
node = self.nodes[i]
node_cpu = int(node['Resources']['CPU'])
bundles.append({device_type: nproc_per_node, 'CPU': node_cpu // 2 + 1})
cpu_bundles.append({'CPU': node_cpu // 4 + 1}) # TODO dynamic scheduling
nproc_cpu_per_node = cpu_proc_count // len(cpu_bundles) + 1
self.cpu_node_map = {}
for i in range(cpu_proc_count):
node_idx = i // nproc_cpu_per_node
cpu_cnt = cpu_bundles[node_idx]['CPU']
self.cpu_node_map[i] = (node_idx, cpu_cnt // nproc_cpu_per_node)
self.placement_groups = [ray.util.placement_group([bundle]) for bundle in bundles]
self.cpu_placement_groups = [ray.util.placement_group([bundle]) for bundle in cpu_bundles]
cpu_bundles.sort(key=lambda bundle: bundle['CPU'], reverse=True)
ray.get([pg.ready() for pg in self.placement_groups])
ray.get([pg.ready() for pg in self.cpu_placement_groups])
self.node_ranks = ray.get(
[ray.remote(get_node_rank).options(placement_group=pg).remote() for pg in self.placement_groups])
if self.node_ranks.count(0) > 1:
self.node_ranks = list(range(len(self.placement_groups)))
self.node2pg: Dict[int, PlacementGroup] = {}
for node_rank, placement_group in zip(self.node_ranks, self.placement_groups):
self.node2pg[node_rank] = placement_group
self.device_groups = {}
ray_address = str(ray.get_runtime_context().gcs_address)
for group_name, group in groups.items():
if group_name in self.possible_keys:
continue
if group['device'] != 'CPU':
ranks = group['ranks']
local_device_groups = []
for rank in ranks:
node_rank = rank // nproc_per_node
gpu_rank = rank % nproc_per_node
local_device_groups.append(
dict(
node_rank=node_rank,
gpu_rank=[gpu_rank],
placement_group=self.node2pg[node_rank],
ray_address=ray_address))
for worker in group['workers']:
self.device_groups[worker] = local_device_groups
else:
ranks = group['ranks']
local_device_groups = []
global_cpu_proc_idx = 0
for _ in range(ranks):
local_device_groups.append(
dict(
placement_group=self.cpu_placement_groups[self.cpu_node_map[global_cpu_proc_idx][0]],
ray_address=ray_address))
global_cpu_proc_idx += 1
for worker in group['workers']:
self.device_groups[worker] = local_device_groups
self.groups = groups
def resource(self, worker):
return self.device_groups[worker]
def destroy_placement_group(self):
import ray
for pg in self.placement_groups:
ray.util.remove_placement_group(pg)
for pg in self.cpu_placement_groups:
ray.util.remove_placement_group(pg)