Coverage for / home / jenkins / .local / lib / python3.10 / site-packages / hyper_parallel / trainer / runtime / device.py: 37%
63 statements
« prev ^ index » next coverage.py v7.13.1, created at 2026-10-25 05:45 +0800
« prev ^ index » next coverage.py v7.13.1, created at 2026-10-25 05:45 +0800
1# Copyright 2025-2026 Bytedance Ltd. and/or its affiliates
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7# http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
15# Following codes are inspired from https://github.com/volcengine/verl/blob/main/verl/utils/device.py
17"""Trainer-side runtime device API.
19Split out of the former ``auto_models/components/utils/device.py`` in stage 7
20(05 §10.4): runtime device selection belongs to the Trainer. The
21accelerator-discovery primitives (``get_device_type`` / ``get_torch_device``
22/ ``get_device_id`` and the availability flags) are single-sourced in
23``hyper_parallel.models.build_options`` — the Trainer-independent home
24that model construction uses when no explicit device option is given — and
25re-exported here so Trainer consumers have one import site.
26"""
28__all__ = [
29 "IS_CUDA_AVAILABLE",
30 "IS_NPU_AVAILABLE",
31 "get_device_type",
32 "get_device_name",
33 "get_torch_device",
34 "get_device_id",
35 "get_dist_comm_backend",
36 "synchronize",
37 "stream_synchronize",
38 "empty_cache",
39 "set_device",
40 "get_device_rng_state",
41 "set_device_rng_state",
42 "is_nccl_backend",
43 "is_hccl_backend",
44 "get_gpu_compute_capability",
45 "is_sm90_or_above",
46 "get_compute_units",
47]
49import logging
50from typing import Any
52import torch
54from hyper_parallel.models.build_options import (
55 IS_CUDA_AVAILABLE,
56 IS_NPU_AVAILABLE,
57 get_device_id,
58 get_device_type,
59 get_torch_device,
60)
63logger = logging.getLogger(__name__)
66def get_device_name() -> str:
67 """Get real device name, e.g. A100, H100"""
68 return get_torch_device().get_device_name()
71def get_dist_comm_backend() -> str:
72 """Return distributed communication backend type based on device type."""
73 if IS_CUDA_AVAILABLE:
74 return "nccl"
75 if IS_NPU_AVAILABLE:
76 return "hccl"
77 raise RuntimeError(f"No available distributed communication backend found on device type {get_device_type()}.")
80def synchronize() -> None:
81 """Execute torch synchronize operation."""
82 get_torch_device().synchronize()
85def stream_synchronize() -> None:
86 """Execute device stream synchronize operation."""
87 if IS_CUDA_AVAILABLE:
88 torch.cuda.current_stream().synchronize()
89 elif IS_NPU_AVAILABLE:
90 torch.npu.current_stream().synchronize()
91 else:
92 synchronize()
95def empty_cache() -> None:
96 """Execute torch empty cache operation."""
97 get_torch_device().empty_cache()
100def set_device(device: torch.types.Device) -> None:
101 """Execute set device operation."""
102 get_torch_device().set_device(device)
105def get_device_rng_state() -> Any:
106 """Return the current accelerator RNG state, or ``None`` when unavailable.
108 Checkpointing needs the device RNG alongside the CPU one: dropout,
109 ``torch.randn`` initializers and any device-side sampling draw from it, so a
110 resume that only restores ``torch.get_rng_state()`` diverges from the
111 original run.
112 """
113 if get_device_type() == "cpu":
114 return None
115 get_rng_state = getattr(get_torch_device(), "get_rng_state", None)
116 if get_rng_state is None:
117 return None
118 return get_rng_state()
121def set_device_rng_state(state: Any) -> None:
122 """Restore an accelerator RNG state captured by :func:`get_device_rng_state`."""
123 if state is None or get_device_type() == "cpu":
124 return
125 set_rng_state = getattr(get_torch_device(), "set_rng_state", None)
126 if set_rng_state is None:
127 logger.warning("Device namespace has no set_rng_state; device RNG not restored.")
128 return
129 set_rng_state(state)
132def is_nccl_backend(backend: str | None = None) -> bool:
133 """Check if the distributed communication backend is NCCL."""
134 return (backend or get_dist_comm_backend()) == "nccl"
137def is_hccl_backend() -> bool:
138 """Check if the distributed communication backend is HCCL."""
139 return get_dist_comm_backend() == "hccl"
142def get_gpu_compute_capability(device: torch.types.Device | int | None = None) -> int:
143 """Return the compute capability as an integer (e.g. 70, 80, 90), or 0 if no GPU."""
144 if not IS_CUDA_AVAILABLE:
145 return 0
146 major, minor = torch.cuda.get_device_capability(device)
147 return major * 10 + minor
150def is_sm90_or_above() -> bool:
151 """Check if the current CUDA device has SM90+ capability."""
152 return get_gpu_compute_capability() >= 90
155def get_compute_units() -> int:
156 """
157 Returns the number of streaming multiprocessors (SMs) or equivalent compute units
158 for the available accelerator.
159 """
160 device_type = getattr(torch.accelerator.current_accelerator(), "type", "cpu")
162 if device_type == "cuda":
163 device_properties = torch.cuda.get_device_properties(0)
164 num_compute_units = device_properties.multi_processor_count
165 elif device_type == "xpu":
166 device_properties = torch.xpu.get_device_properties(0)
167 num_compute_units = device_properties.max_compute_units
168 else:
169 logger.warning("No CUDA or XPU device available. Using CPU.")
170 num_compute_units = torch.get_num_threads()
172 return num_compute_units