Coverage for / home / jenkins / .local / lib / python3.10 / site-packages / hyper_parallel / core / fully_shard / hsdp_state.py: 91%
98 statements
« prev ^ index » next coverage.py v7.13.1, created at 2026-07-06 05:41 +0800
« prev ^ index » next coverage.py v7.13.1, created at 2026-07-06 05:41 +0800
1# Copyright 2025-2026 Huawei Technologies Co., Ltd
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.
14# ============================================================================
15"""HSDP cell state"""
16from typing import List, Tuple, Union
18from hyper_parallel.platform import get_platform
19from hyper_parallel.core.fully_shard.hsdp_param import HSDPParamV2
20from hyper_parallel.core.fully_shard.hsdp_utils import HSDPConfigV2, ShardedState
21from hyper_parallel.tools.logging import get_logger
23logger = get_logger("FSDP")
25platform = get_platform()
28class HSDPState:
29 """HSDP state for cell"""
30 # Record pending per-parameter reduce-scatter/all-reduce work across
31 # fully_shard states so later backward hooks/root drains can materialize
32 # gradients launched by earlier states.
33 pre_reduce_scatter_params = []
34 pre_all_reduce_params = []
36 def __init__(self, cell: Union[platform.Module, Tuple[platform.Module, ...]], mesh_info,
37 config: HSDPConfigV2, platform_impl, device=None):
38 """
39 Initialize HSDPState.
41 Args:
42 cell (platform.Module or Tuple[platform.Module, ...]): The module(s) whose parameters
43 are managed by this state. When a tuple is passed, all modules are
44 treated as one FSDP unit.
45 mesh_info: Mesh topology for shard/replicate dimensions.
46 config (HSDPConfigV2): HSDP configuration (mesh, mp_policy, offload_policy, etc.).
47 platform_impl: Platform abstraction layer (Torch or MindSpore).
48 device (torch.device, optional): Target device for parameters.
49 """
50 self.modules = (cell,) if isinstance(cell, platform.Module) else tuple(cell)
51 self.cell = self.modules[0]
52 self.mesh_info = mesh_info
53 self.config = config
54 self.mp_policy = config.mp_policy
55 self.offload_policy = config.offload_policy
56 self.platform = platform_impl
57 self.device = device
58 self.hsdp_params: List[HSDPParamV2] = []
59 self.sharded_hsdp_params: List[HSDPParamV2] = []
60 self.replicate_params: List[HSDPParamV2] = []
61 self._move_states_to_device()
62 self._init_hsdp_params()
63 self.is_shard = True
64 self.is_replicate_shard = True
65 self.module_name = None
67 def __repr__(self) -> str:
68 """Stable debug name used in log lines.
70 ``module_name`` is only assigned by the root forward pre-hook, so fall
71 back to the managed module class and object id before names are set.
72 Logging's ``%s`` calls this lazily -- only when a record is emitted.
73 """
74 if self.module_name:
75 return str(self.module_name)
76 return f"{self.cell.__class__.__name__}@{id(self.cell):x}"
78 def _init_hsdp_params(self):
79 """init hsdp parameters for cell"""
80 raise NotImplementedError("HSDPState subclasses must implement _init_hsdp_params")
82 def _move_states_to_device(self):
83 """move states to device"""
84 raise NotImplementedError("HSDPState subclasses must implement _move_states_to_device")
86 def _assert_replicate_params_unsharded(self) -> None:
87 """Validate replicate params are already materialized when state says so."""
88 for param in self.replicate_params:
89 sharded_state = getattr(param, "sharded_state", None)
90 if sharded_state != ShardedState.UNSHARDED:
91 param_fqn = getattr(param, "_param_fqn", "<unknown>")
92 raise AssertionError(
93 f"Expected replicate parameter {param_fqn} to be "
94 f"{ShardedState.UNSHARDED}, got {sharded_state}"
95 )
97 def shard(self, shard_replicate: bool = True):
98 """change parameters to sharded state"""
99 logger.debug(
100 "action=reshard module=%s shard_params=%s replicate_params=%s shard_replicate=%s",
101 self,
102 self.sharded_hsdp_params,
103 self.replicate_params,
104 shard_replicate,
105 )
106 if not self.is_shard:
107 for param in self.sharded_hsdp_params:
108 param.to_sharded()
109 self.is_shard = True
110 if shard_replicate and not self.is_replicate_shard:
111 for param in self.replicate_params:
112 param.to_sharded()
113 self.is_replicate_shard = True
115 def unshard(self, async_op=False, unshard_replicate: bool = True):
116 """change parameters to unsharded state"""
117 logger.debug(
118 "action=unshard module=%s async_op=%s shard_params=%s replicate_params=%s unshard_replicate=%s",
119 self,
120 async_op,
121 self.sharded_hsdp_params,
122 self.replicate_params,
123 unshard_replicate,
124 )
125 if not self.is_shard and (not unshard_replicate or not self.is_replicate_shard):
126 if unshard_replicate:
127 self._assert_replicate_params_unsharded()
128 return
130 if unshard_replicate:
131 if self.is_replicate_shard:
132 for param in self.replicate_params:
133 param.unshard(async_op)
134 else:
135 self._assert_replicate_params_unsharded()
136 if self.is_shard:
137 if self.config.comm_fusion and self.param_group is not None:
138 self.param_group.unshard(async_op)
139 else:
140 for param in self.sharded_hsdp_params:
141 param.unshard(async_op)
142 if not async_op:
143 self.wait_for_unshard(unshard_replicate)
145 def prefetch(self, unshard_replicate: bool = True):
146 """prefetch unsharded parameters"""
147 logger.debug(
148 "action=prefetch module=%s shard_params=%s replicate_params=%s unshard_replicate=%s",
149 self,
150 self.sharded_hsdp_params,
151 self.replicate_params,
152 unshard_replicate,
153 )
154 self.unshard(async_op=True, unshard_replicate=unshard_replicate)
156 def wait_for_unshard(self, wait_for_replicate: bool = True):
157 """wait for all unshard parameters"""
158 logger.debug(
159 "action=wait_unshard module=%s shard_params=%s replicate_params=%s wait_for_replicate=%s",
160 self,
161 self.sharded_hsdp_params,
162 self.replicate_params,
163 wait_for_replicate,
164 )
165 if not self.is_shard and (not wait_for_replicate or not self.is_replicate_shard):
166 if wait_for_replicate:
167 self._assert_replicate_params_unsharded()
168 return
169 if wait_for_replicate:
170 if self.is_replicate_shard:
171 for param in self.replicate_params:
172 param.wait_for_unshard()
173 self.is_replicate_shard = False
174 else:
175 self._assert_replicate_params_unsharded()
176 if self.is_shard:
177 if self.config.comm_fusion and self.param_group is not None:
178 self.param_group.wait_for_unshard()
179 else:
180 for param in self.sharded_hsdp_params:
181 param.wait_for_unshard()
182 self.is_shard = False
184 def set_gradient_scaling_factor(self, factor):
185 """Propagate the gradient scaling factor to the layer that applies it.
187 The factor is consumed on the reduce input: ``param_group.foreach_reduce``
188 for the fused (comm_fusion) path, or per-parameter ``reduce_scatter_grad``
189 / ``all_reduce_grad`` otherwise. The state does not hold a copy.
190 """
191 param_group = getattr(self, "param_group", None)
192 if param_group is not None:
193 param_group.gradient_scaling_factor = factor
194 else:
195 for hsdp_param in self._iter_managed_params():
196 hsdp_param.gradient_scaling_factor = factor
198 def _iter_managed_params(self):
199 """Return all fully_shard-managed parameters, including replicate_params."""
200 return [*self.hsdp_params, *self.replicate_params]