Diff Coverage

Diff: origin/r1.0.0...HEAD, staged and unstaged changes

Source File Diff Coverage (%) Missing Lines
hyper_parallel/auto_parallel/sapp_nd/memory_estimation/_backbone.py 16.7% 103-107
hyper_parallel/auto_parallel/sapp_nd/memory_estimation/_bwd_overhead.py 69.2% 170,178,192,200
hyper_parallel/auto_parallel/sapp_nd/memory_estimation/_func_tracer.py 0.0% 126
hyper_parallel/auto_parallel/sapp_nd/memory_estimation/demo.py 0.0% 16,21,24,26-27,30,37,41,44,47,49,52,54,56,58,60,62,64,67,70,72,75,78,81,84-86,88,90,92,96,100,103-104
hyper_parallel/auto_parallel/sapp_nd/memory_estimation/hooks/template.py 100%  
hyper_parallel/auto_parallel/sapp_nd/nd/common/_cost_model_variables.py 100%  
hyper_parallel/auto_parallel/sapp_nd/nd/common/arch_hooks.py 100%  
hyper_parallel/auto_parallel/sapp_nd/nd/common/cost_model_preprocess.py 50.0% 91,94-95
hyper_parallel/auto_parallel/sapp_nd/nd/common/framework_parsers/cost_model_parser_hyperparallel.py 58.3% 39-41,47-48
hyper_parallel/auto_parallel/sapp_nd/nd/common/framework_parsers/cost_model_parser_mindspeed.py 100%  
hyper_parallel/auto_parallel/sapp_nd/nd/common/hardware.py 50.0% 36
hyper_parallel/auto_parallel/sapp_nd/nd/debug.py 100%  
hyper_parallel/auto_parallel/sapp_nd/nd/parallelize.py 0.0% 484
hyper_parallel/auto_parallel/sapp_nd/perf_estimation/comm_time.py 100%  
hyper_parallel/auto_parallel/sapp_nd/perf_estimation/estimate.py 92.3% 268
hyper_parallel/auto_parallel/sapp_ppb/sapp/sapp_pipeline.py 100%  
hyper_parallel/auto_parallel/sapp_ppb/sapp/sapp_solver.py 86.5% 516,524-528,531
hyper_parallel/auto_parallel/sapp_ppb/simulator/plot_manager.py 54.5% 162,165,168,179,202
hyper_parallel/auto_parallel/sapp_ppb/simulator/pp_simulator.py 93.8% 464
hyper_parallel/auto_parallel/sapp_ppb/utils/computation_analyzer.py 100%  
hyper_parallel/auto_parallel/sapp_ppb/utils/config.py 100%  
hyper_parallel/core/pipeline_parallel/mpipe/__init__.py 100%  
hyper_parallel/auto_parallel/sapp_nd/memory_estimation/_backbone.py
 99
100
101
102
103
104
105
106
107
108
109
110
111
                        continue
                    with open(spec.origin, "r", encoding="utf-8") as mf:
                        source = mf.read()
                        tree = ast.parse(source)
                        mod_cls = None
                        for node in ast.walk(tree):
                            if isinstance(node, ast.ClassDef) and node.name == hook_cls:
                                mod_cls = node
                                break
                        if mod_cls:
                            target_mod_path = mod_path
                            break
            if target_mod_path:
hyper_parallel/auto_parallel/sapp_nd/memory_estimation/_bwd_overhead.py
166
167
168
169
170
171
172
173
174
            result = self._fetch_node_and_switch_env(
                stages, record_lay_types, stage_id, 0, lay_id
            )
            if result is None:
                raise RuntimeError("_fetch_node_and_switch_env returned None.")
            fwd_first += sum(self._inner_dynamic_mem(default_micro_factor=1))
        for lay_id, lay in enumerate(stages[stage_id][1]):
            self._ctx.current_node = LayerType.NOT_REC_LAYER
            result = self._fetch_node_and_switch_env(
174
175
176
177
178
179
180
181
182
            result = self._fetch_node_and_switch_env(
                stages, record_lay_types, stage_id, 1, lay_id
            )
            if result is None:
                raise RuntimeError("_fetch_node_and_switch_env returned None.")
            if lay == LayerType.FULL_REC_LAYER:
                bwd_last = max(
                    bwd_last,
                    sum(self._inner_dynamic_mem(default_micro_factor=1)),
188
189
190
191
192
193
194
195
196
            result = self._fetch_node_and_switch_env(
                stages, record_lay_types, stage_id, 1, lay_id
            )
            if result is None:
                raise RuntimeError("_fetch_node_and_switch_env returned None.")
            fwd_last = sum(self._inner_dynamic_mem(default_micro_factor=1))
        for lay_id, lay in enumerate(stages[stage_id][0]):
            self._ctx.current_node = LayerType.NOT_REC_LAYER
            result = self._fetch_node_and_switch_env(
196
197
198
199
200
201
202
203
204
            result = self._fetch_node_and_switch_env(
                stages, record_lay_types, stage_id, 0, lay_id
            )
            if result is None:
                raise RuntimeError("_fetch_node_and_switch_env returned None.")
            if lay == LayerType.FULL_REC_LAYER:
                bwd_first = max(
                    bwd_first,
                    sum(self._inner_dynamic_mem(default_micro_factor=1)),
hyper_parallel/auto_parallel/sapp_nd/memory_estimation/_func_tracer.py
122
123
124
125
126
127
128
129
130
                        node = self.fetch_node_from_lineno(frame.f_lineno, co)
                        if not node:
                            return self.line_tracer
                        line = ast.unparse(node)
                    sign = next(op for op in op_equals if op in line)
                    left, right = line.split(sign, 1)
                    left, right = left.strip(), right.strip()
                    if sign != "=":
                        right = f"{left} {sign[0]} {right}"
hyper_parallel/auto_parallel/sapp_nd/memory_estimation/demo.py
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
# See the License for the specific language governing permissions and
# limitations under the License.
# ============================================================================
"""Walkthrough of the EvaluatorV2 memory estimation API, see README.md."""
from typing import Any

from hyper_parallel.auto_parallel.sapp_nd.nd.common.layer_type import LayerType
from hyper_parallel.auto_parallel.sapp_nd.memory_estimation.estimate_v2 import EvaluatorV2
from hyper_parallel.auto_parallel.sapp_nd.memory_estimation.hooks.template import Template
from hyper_parallel.auto_parallel.sapp_nd.memory_estimation.logger import logger


def my_attn_num_param(ccfg: Any, ctx: Any) -> float:
    """Attention parameter count that overrides the default formula."""
    del ctx
    return 10 * ccfg.h * ccfg.h


def custom(ccfg: Any) -> None:
    """Cost model variables that override the parsed configuration."""
    ccfg.bytes_compute = 1
    ccfg.s = 1024
    ccfg.n_attMM = 5
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
    ccfg.s = 1024
    ccfg.n_attMM = 5


def main() -> None:
    """Estimate, inspect and customize the memory of the bundled test cases."""
    # Instantiate evaluator with a model configuration,
    #  log_level=0 removes warning messages
    e = EvaluatorV2("./test_cases/mixtral/default.yaml", log_level=0)

    # Check all defined node type
    logger.output("%s", list(LayerType))

    # Estimate peak memory (in Megabytes)
    peak_mem = e.estimate_peak(verbose=True)
    # Check whether estimation fits in device's max memory
    e.mem_fit(peak_mem)

    # Estimate static memory of a specific pipeline stage (in Megabytes)
    logger.output("%s", e.static_mem_stage(1))
    # Estimate dynamic memory of a specific pipeline stage (in Megabytes)
    logger.output("%s", e.dynamic_mem_stage(1))
    # Estimate static memory of a specific layer and stage (in Megabytes)
    logger.output("%s", e.static_mem_layer(LayerType.FULL_REC_LAYER, 1))
    # Estimate dynamic memory of a specific layer and stage (in Megabytes)
    logger.output("%s", e.dynamic_mem_layer(LayerType.FULL_REC_LAYER, 1))
    # Retrieve the memory estimation logs of a specific stage (in Megabytes)
    logger.output("%s", e.logs_mem_stage(1))
    # Fetch memory insights from each pipeline stage
    logger.output("%s", e.estimate_peak_insight())
    # PPB Input
    logger.output("%s", e.estimate_layer_memory())

    # Inspect a specific stage (here is the first one)
    e.estimate_peak(spec_stage_id=0, verbose=True)

    # Plot
    e.estimate_peak(plot=True)

    e = EvaluatorV2("./test_cases/deepseek3/default.yaml", log_level=0)

    # Overwriting context function
    e.set_attn_eval_fun(num_p=my_attn_num_param)

    # Overwriting a training feature
    e.set_passes(swap_os=True)

    # Overwriting cost model variables
    e.set_ccfg(custom)

    # Overwriting strategy
    logger.output("%s", e.get_strategy())
    e.set_strategy(dp=8, tp=8, m=128)
    logger.output("%s", e.get_strategy())

    e.estimate_peak(verbose=True)
    # Inspect ccfg object (cost model variables)
    e.print_ccfg()
    # Inspect ctx object (evaluation variables and functions)
    e.print_ctx()

    # Load a hook class
    # ... when declaring an Evaluator
    e = EvaluatorV2(
        "./test_cases/deepseek3/default.yaml", log_level=0, hook_cls=Template()
    )
    # ... by using load_hook_cls()
    e.load_hook_cls(Template())


if __name__ == "__main__":
    main()
hyper_parallel/auto_parallel/sapp_nd/nd/common/cost_model_preprocess.py
87
88
89
90
91
92
93
94
95
96
97
98
            chunk_lay_occ = []
            if chunk:
                layer, count = chunk[0], 1
                for lay_id in range(1, len(chunk)):
                    if chunk[lay_id] == layer:
                        count += 1
                    else:
                        chunk_lay_occ += [f"{count}{layer.name[0]}"]
                        layer, count = chunk[lay_id], 1
                chunk_lay_occ += [f"{count}{layer.name[0]}"]
            stage_layers += [chunk_lay_occ]
        logger.info("stage _%s : %s", stage_id, stage_layers)
hyper_parallel/auto_parallel/sapp_nd/nd/common/framework_parsers/cost_model_parser_hyperparallel.py
35
36
37
38
39
40
41
42
43
44
45
            self.ccfg.specs = specs
            self.__parse_toml()
        else:
            now = time.time()
            home_path = os.path.expanduser("~")
            if home_path not in sys.path:
                sys.path.append(home_path)
            spec_torch = importlib.util.find_spec("torch")
            spec_torchtitan = importlib.util.find_spec("torchtitan")
            if spec_torch is not None and spec_torchtitan is not None:
                # existing torchtitan package
43
44
45
46
47
48
49
50
51
52
            spec_torchtitan = importlib.util.find_spec("torchtitan")
            if spec_torch is not None and spec_torchtitan is not None:
                # existing torchtitan package
                spec_path = spec_torchtitan.submodule_search_locations[0]
                if spec_path not in sys.path:
                    sys.path.append(spec_path)
                try:
                    logger.info(
                        "found torchtitan package from homedir: %s",
                        spec_path,
hyper_parallel/auto_parallel/sapp_nd/nd/common/hardware.py
32
33
34
35
36
37
38
39
40
        self.name = name
        self.level_bound_number = bounds
        self.level_bandwidth = bandwidths
        if len(bounds) != len(bandwidths):
            raise ValueError("bounds and bandwidths must have the same length")
        self.levels = len(bounds)

    def __str__(self):
        return self.name
hyper_parallel/auto_parallel/sapp_nd/nd/parallelize.py
480
481
482
483
484
485
486
487
488
            str(self.config.dimensions),
        )
        if self.enable_debug:
            file_path = os.path.dirname(os.path.realpath(__file__))
            output_path = os.path.join(file_path, "output")
            if scored_space:
                Debug.plot_nd(
                    scored_space,
                    output_path,
hyper_parallel/auto_parallel/sapp_nd/perf_estimation/estimate.py
264
265
266
267
268
269
270
271
272
    non_steady_perf = 0
    steady_perf = 0
    if cfg.p == 1:
        if len(stage_perfs) != 1:
            raise ValueError("Expected exactly one stage performance")
        steady_perf = sum_time * cfg.m
    elif cfg.vp == 1:
        non_steady_perf = sum_time
        if GENERALIZE_PIPELINE_CALCULATION:
hyper_parallel/auto_parallel/sapp_ppb/sapp/sapp_solver.py
512
513
514
515
516
517
518
519
520
        return None

    def _current_layer_sum(self, variables, layer, interleave, stage_range):
        """Sum current interleave variables over a stage range."""
        considered = Recompute.get_used_list(self.recompute_considered_)
        return lpSolver.lpSum(
            variables[layer][rec][interleave][stage]
            for rec in considered for stage in stage_range
        )
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
        )

    def _previous_layer_sum(self, variables, layer, interleave):
        """Sum variables from previous interleaves."""
        considered = Recompute.get_used_list(self.recompute_considered_)
        previous_variables = []
        for rec in considered:
            for prev_interleave in range(interleave):
                previous_variables.extend(
                    variables[layer][rec][prev_interleave][stage] for stage in range(self.num_of_stage_)
                )
        return lpSolver.lpSum(previous_variables)

    def _add_frontier_upper_bounds(self, prob, variables, sorted_layers):
        """Prevent previous body layer types after each multimodal frontier."""
        for frontier in range(1, len(sorted_layers[Layer.type_enum.BODY])):
hyper_parallel/auto_parallel/sapp_ppb/simulator/plot_manager.py
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
        block_index = self._get_block_indices(blocks, mode=mode, equal_wide=equal_wide)
        width = max(np.max(block_index[p]) for p in range(pp)) if blocks[0][-1].end is None \
            else max(blocks[p][-1].end for p in range(pp))
        if self.draw_block(block_index, blocks, ax_index, equal_wide, width, phase=phase) is not self:
            raise ValueError("Drawing blocks must return the current plot manager")
        if comm:
            if self.draw_comm(block_index, blocks, ax_index, equal_wide, mode) is not self:
                raise ValueError("Drawing communications must return the current plot manager")
        if connect:
            if self.draw_connect(block_index, blocks, ax_index, equal_wide, mode) is not self:
                raise ValueError("Drawing connections must return the current plot manager")
        self._set_block_ax(self.ax[ax_index], pp)
        self.ax[ax_index].set_xlim(0, width)
        self.ax[ax_index].set_xticks(np.linspace(0, width, 8))
        return self
175
176
177
178
179
180
181
182
183
                  ax_index: int = 0, comm: bool = False, connect: bool = False,
                  equal_wide: bool = False) -> "PlotMgr":
        """Highlight a dependency loop (non-comm) with red arrows and a textual trace."""
        if self.draw(blocks, ax_index, comm, connect, equal_wide, phase=True) is not self:
            raise ValueError("Drawing a dependency loop must return the current plot manager")
        block_index = self._get_block_indices(blocks, equal_wide=equal_wide)
        msg = 'dependency loop: '
        for b in range(len(loop) - 1):
            p = loop[b].stage
198
199
200
201
202
203
204
205
206
    def draw_comm_loop(self, lines: List[List[BlockSim]], loop: List[BlockSim],
                       ax_index: int = 0) -> "PlotMgr":
        """Highlight a dependency loop in the send-receive graph."""
        if self.draw(lines, ax_index, True, True, True, 'joint', phase=True) is not self:
            raise ValueError("Drawing a communication loop must return the current plot manager")
        block_index = self._get_block_indices(lines, mode='joint', equal_wide=True)
        msg = 'dependency loop: '
        for b in range(len(loop) - 1):
            p = loop[b].stage
hyper_parallel/auto_parallel/sapp_ppb/simulator/pp_simulator.py
460
461
462
463
464
465
466
467
468
            if p % 2 == 0 and lines[p][i_b + 1].type == 'r' and lines[p][i_b + 2].type == 's':
                lines[p][i_b + 1], lines[p][i_b + 2] = lines[p][i_b + 2], lines[p][i_b + 1]
            if p % 2 == 1 and lines[p][i_b + 1].type == 's' and lines[p][i_b + 2].type == 'r':
                if block.phase == 'warmup' and self.blocks[p][b + 1].phase == 'cooldown':
                    return
                lines[p][i_b + 1], lines[p][i_b + 2] = lines[p][i_b + 2], lines[p][i_b + 1]
            if lines[p][i_b + 1].dual.stage == lines[p][i_b + 2].dual.stage:
                pd = lines[p][i_b + 1].dual.stage
                j_b1 = lines[pd].index(lines[p][i_b + 1].dual)