Coverage for / home / jenkins / .local / lib / python3.10 / site-packages / hyper_parallel / distributed / apply.py: 97%
34 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 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"""apply: public entry point for applying a ShardingPlan (05 §4 canonical).
17apply_sharding_plan: Phase 0 normalization -> A parameter sharding -> B special
18handlers -> C entry unpack + source_shard_info -> C forward wrapping
19(production/validate/moe/cp/vocab_embed, five paths) -> D tied weights.
21Dual-mode architecture constraint (05 §1.4): production has zero DTensor
22dispatch (build-time unpack + PrecompiledBoundary); the only difference between
23validate and production is the boundary stitching method -- for any module whose
24DTensor dispatch hides data-dependent logic (embedding mask / attention K/V
25gather / MoE all-to-all), both modes explicitly reconstruct it with the same
26local-region wrapper (D-01''/D-02/D-03').
28This module is a thin public facade: the implementation lives in
29``_builder/applier.py`` (preflight/mesh/phase orchestration),
30``_builder/parameter_sharding.py`` (parameter localize/stack/placement) and
31``_builder/forward_rewriter.py`` (forward mutation).
32"""
34from typing import Any, Optional, Tuple
36from hyper_parallel.distributed.plan import ShardingPlan
37from hyper_parallel.distributed.recipe_spec import (
38 _normalize_out_fields,
39)
40from hyper_parallel.distributed._builder.applier import (
41 _apply_phase_c,
42 _apply_plan_special_handlers,
43 _get_active_mesh,
44 _get_tp_submesh,
45 _preflight_compute_injection,
46)
47from hyper_parallel.distributed._builder.parameter_sharding import (
48 _build_runtime_source_shard_info,
49 _replicate_tied_weights,
50 _resolve_parameter_source_meshes,
51 _shard_planned_parameters,
52 detect_tied_weights,
53)
56def _prepare_sharding_meshes(
57 model: Any,
58 plan: ShardingPlan,
59 mesh: Any,
60) -> Tuple[Any, Any, Any, Any, Any]:
61 """Resolve active and parameter source meshes before plan application."""
62 # Lazy import: components.distributed.__init__ re-exports this module, so
63 # a module-level import of the mesh module would cycle through the
64 # package __init__.
65 from hyper_parallel.distributed.mesh import ( # pylint: disable=C0415
66 MeshContext,
67 )
68 mesh_context = mesh if isinstance(mesh, MeshContext) else None
69 if mesh_context is None:
70 device_mesh = mesh
71 else:
72 device_mesh = mesh_context.device_mesh
73 if device_mesh is None:
74 raise ValueError("apply_sharding_plan requires a DeviceMesh")
76 mesh_dim_names = plan.mesh_dim_names
77 # Active sub-mesh: the planner strips size=1 axes (plan.mesh_dim_names), but the
78 # passed-in mesh may still contain those axes -- placements are resolved against
79 # plan.mesh_dim_names, so the dimensionality must align with the mesh, otherwise
80 # distribute_tensor will silently shard along the wrong axis.
81 full_mesh = device_mesh
82 active_mesh = _get_active_mesh(device_mesh, mesh_dim_names)
83 tp_mesh = _get_tp_submesh(active_mesh, mesh_dim_names)
84 models = model if isinstance(model, list) else [model]
86 # Explicit-injection guard: CP/EP sharding without an explicit compute
87 # injection fails fast here, BEFORE any parameter is touched
88 _preflight_compute_injection(plan, active_mesh, model=models[0])
90 expert_mesh, dense_source_mesh, expert_source_mesh = (
91 _resolve_parameter_source_meshes(plan, mesh_context, full_mesh, tp_mesh)
92 )
93 return active_mesh, models, expert_mesh, dense_source_mesh, expert_source_mesh
96def apply_sharding_plan(
97 model: Any,
98 plan: ShardingPlan,
99 mesh: Any,
100 *,
101 validate_mode: bool = False,
102) -> Tuple[Any, Optional[Any]]:
103 """Apply a ShardingPlan using a DeviceMesh or MeshContext.
105 Args:
106 model: The model to shard (an HF-style ``nn.Module``, or a list of
107 per-part models in PP scenarios).
108 plan: The :class:`ShardingPlan` produced by the planner.
109 mesh: A ``DeviceMesh`` or a :class:`MeshContext` carrying one.
110 validate_mode: When ``True``, keep parameters as DTensors and wrap
111 forwards for placement-propagation validation instead of the
112 production local-tensor path.
114 Returns (model, source_shard_info):
115 - production: at the Phase C entry, a one-shot `_local_params_context` permanently
116 unwraps DTensor parameters into plain local tensors, and builds source_shard_info
117 for fully_shard to use. Entries record the complete source layout:
118 ``{param_fqn: (placements_tuple, source_sub_mesh)}`` with one placement per
119 non-FSDP source axis (dense entries derive from the dense-FSDP mesh, routed
120 experts from the expert mesh);
121 - validate: no unwrap (parameters remain DTensors); source_shard_info is None.
122 """
123 mesh, models, expert_mesh, dense_source_mesh, expert_source_mesh = (
124 _prepare_sharding_meshes(model, plan, mesh)
125 )
127 # ====== Phase 0: normalize out_src/out_dst scalar shorthand (idempotent, covers user-injected paths) ======
128 for spec in plan.modules.values():
129 _normalize_out_fields(spec)
131 # ====== Phase A: parameter sharding ======
132 _shard_planned_parameters(models, plan, mesh, expert_mesh, validate_mode)
134 # ====== Phase B: special handlers ======
135 _apply_plan_special_handlers(models, plan, mesh)
137 # ====== Phase C entry: one-shot unpack at build time (production only) ======
138 source_shard_info = _build_runtime_source_shard_info(
139 models,
140 plan,
141 dense_source_mesh,
142 expert_source_mesh,
143 validate_mode,
144 )
146 # ====== Phase C: wrap forward ======
147 for part in models:
148 _apply_phase_c(part, plan, mesh, validate_mode, expert_mesh=expert_mesh)
150 # ====== Phase D: tied weights ======
151 tied_pairs = list(plan.tied_pairs) or detect_tied_weights(models[0])
152 for part in models:
153 _replicate_tied_weights(part, tied_pairs)
155 return model, source_shard_info