Coverage for / home / jenkins / .local / lib / python3.10 / site-packages / hyper_parallel / auto_parallel / sapp_ppb / pp_optimizer.py: 80%
54 statements
« prev ^ index » next coverage.py v7.13.1, created at 2026-08-21 04:29 +0800
« prev ^ index » next coverage.py v7.13.1, created at 2026-08-21 04:29 +0800
1# Copyright 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"""PP optimizer — YAML + JSON driven pipeline parallelism strategy optimizer."""
17from __future__ import annotations
19import logging
20import math
21from typing import Any
23from hyper_parallel.auto_parallel.sapp_ppb.pp_config_builder.layer_loader import (
24 LayerBuilder,
25)
26from hyper_parallel.auto_parallel.sapp_ppb.pp_modeling.pp_structs import (
27 PPStrategyResult,
28)
29from hyper_parallel.auto_parallel.sapp_ppb.pp_modeling.pp_balancer import (
30 PPBalancer,
31)
32from hyper_parallel.auto_parallel.sapp_ppb.pp_config_builder.yaml_parser import (
33 parse_yaml_for_optimization,
34)
35from hyper_parallel.auto_parallel.sapp_ppb.pp_sim_adapter import PPSimulator
38class PPOptimizer:
39 """Unified PP strategy optimizer — YAML + JSON driven.
41 Pipeline topology and ILP constraints (pp_degree, num_layer,
42 micro_batch_num, num_of_interleave, memory_limit, etc.) come from
43 a YAML file; layer descriptions come from a JSON file.
45 Orchestration flow:
47 1. Parse YAML config and build layers from JSON.
48 2. Call :class:`PPBalancer` to solve the ILP and build the
49 strategy result.
50 3. If ``enable_simulation`` is ``True``, run :class:`PPSimulator`
51 and merge simulation metrics into the result.
53 Example:
54 >>> optimizer = PPOptimizer()
55 >>> result = optimizer.optimize(
56 ... yaml_path="init_demo.yaml",
57 ... json_path="demo.json",
58 ... )
59 """
61 def optimize(
62 self,
63 yaml_path: str = "",
64 json_path: str = "",
65 ) -> PPStrategyResult:
66 """Optimize PP strategy — YAML + JSON driven.
68 Reads pipeline topology and ILP constraints from ``yaml_path``
69 and layer descriptions from ``json_path``, then runs a single
70 ILP optimisation, optionally runs the pipeline simulator, and
71 returns the result.
73 Args:
74 yaml_path: Path to the YAML configuration file with pipeline
75 topology and ILP constraints (``pipeline_num``,
76 ``num_layer``, ``micro_batch_num``,
77 ``num_of_interleave``, ``memory_limit``, etc.).
78 json_path: Path to the JSON file with ``layers_description``
79 defining the model layers (HEAD, BODY, TAIL).
81 Returns:
82 Optimised PP strategy result.
84 Raises:
85 ValueError: If ``yaml_path`` or ``json_path`` is empty.
86 RuntimeError: If the ILP solution is infeasible.
88 Example:
89 >>> optimizer = PPOptimizer()
90 >>> result = optimizer.optimize(
91 ... yaml_path="init_demo.yaml",
92 ... json_path="demo.json",
93 ... )
94 """
95 if not yaml_path:
96 raise ValueError(
97 "PPOptimizer.optimize requires yaml_path. "
98 "Please provide a valid YAML configuration path."
99 )
100 if not json_path:
101 raise ValueError(
102 "PPOptimizer.optimize requires json_path. "
103 "Please provide a valid JSON profile path."
104 )
106 yaml_config = parse_yaml_for_optimization(yaml_path)
108 builder = LayerBuilder(yaml_config, json_path)
109 balancer = PPBalancer(builder)
110 result = balancer.balance_with_ilp()
112 if not result.is_feasible:
113 details = result.infeasibility_details
114 msg = f"PP optimization failed: {details.get('reason', 'unknown')}"
115 if details.get("error"):
116 msg += f" ({details['error']})"
117 raise RuntimeError(msg)
119 if yaml_config.enable_simulation:
120 self._run_simulation(
121 result,
122 balancer.pipeline,
123 yaml_config,
124 builder.constant_memory,
125 yaml_config.sim_comm_time,
126 )
128 if result.simulation_status == "failed":
129 logging.getLogger(__name__).warning(
130 "ILP succeeded but simulation failed: %s. "
131 "Pipeline bubble and step time estimates are unavailable.",
132 result.simulation_error,
133 )
134 elif result.simulation_status == "not_run":
135 logging.getLogger(__name__).info(
136 "Simulation skipped (enable_simulation=False). "
137 "Pipeline bubble and step time estimates are not available."
138 )
140 return result
142 @staticmethod
143 def _run_simulation(
144 result: PPStrategyResult,
145 pipeline: Any,
146 yaml_config: Any,
147 constant_memory: int,
148 sim_comm_time: float,
149 ) -> None:
150 """Run pipeline simulator and merge results into *result* in-place.
152 Creates a :class:`PPSimulator`, runs the post-ILP simulation,
153 and merges the simulator output fields into the provided
154 ``result``. On failure, marks ``simulation_status`` as
155 ``"failed"`` with a descriptive error.
157 Args:
158 result: Strategy result from :meth:`PPBalancer.balance_with_ilp`
159 (modified in-place with simulation metrics).
160 pipeline: Solved :class:`SappPipeline` instance.
161 yaml_config: YAML configuration with pipeline topology.
162 constant_memory: Constant memory per stage (MB).
163 sim_comm_time: P2P communication time between adjacent stages
164 (ms) for the simulator.
165 """
166 pp_sim = PPSimulator(
167 pipeline=pipeline,
168 yaml_config=yaml_config,
169 constant_memory=constant_memory,
170 )
172 sim_result = pp_sim.simulate_from_ilp(sim_comm_time=sim_comm_time)
174 if sim_result is None:
175 result.simulation_status = "failed"
176 result.simulation_error = (
177 "ILP simulation returned None (e.g. micro_batch_num < pp_degree)"
178 )
179 return
181 real_bubble_val = sim_result.simulator_bubbles.get("real", 0.0)
182 if not math.isfinite(sim_result.simulator_end_time) or not math.isfinite(real_bubble_val):
183 result.simulation_status = "failed"
184 result.simulation_error = (
185 "Simulation produced non-finite results (e.g. total compute time is zero)"
186 )
187 return
189 result.simulation_status = sim_result.simulation_status
190 result.simulation_error = sim_result.simulation_error
191 result.simulator_end_time = sim_result.simulator_end_time
192 result.simulator_bubbles = sim_result.simulator_bubbles
193 result.simulator_peak_memory = sim_result.simulator_peak_memory
195 pipeline_bubble = sim_result.simulator_bubbles.get("real")
196 if pipeline_bubble is None:
197 logging.getLogger(__name__).warning(
198 "Simulator bubbles dict missing 'real' key. "
199 "Available keys: %s",
200 list(sim_result.simulator_bubbles.keys()),
201 )
202 result.pipeline_bubble = pipeline_bubble