Coverage for / home / jenkins / .local / lib / python3.10 / site-packages / hyper_multicore / _collective.py: 100%
41 statements
« prev ^ index » next coverage.py v7.13.1, created at 2026-11-15 05:03 +0800
« prev ^ index » next coverage.py v7.13.1, created at 2026-11-15 05:03 +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"""Cold-path result exchange and failure diagnostics for Multicore owners."""
17from __future__ import annotations
19from collections.abc import Callable
20from typing import Any
22import torch.distributed as dist
24FailureReport = tuple[str, str]
27class CollectiveConvergenceError(RuntimeError):
28 """Raised when peers cannot exchange collective lifecycle results."""
31def exchange(value: Any, *, group: Any, failure_prefix: str) -> list[Any]:
32 """Exchange picklable results, preserving the original communication error as cause.
34 Args:
35 value: Picklable local result.
36 group: Explicit group; None selects WORLD when initialized.
37 failure_prefix: Caller-owned operation context for communication failures.
39 Returns:
40 Results in group rank order, or the local result when distributed is uninitialized.
41 """
42 try:
43 if not dist.is_initialized():
44 return [value]
45 values = [None] * dist.get_world_size(group)
46 dist.all_gather_object(values, value, group=group)
47 return values
48 except Exception as error: # pylint: disable=broad-exception-caught
49 exception_type, message = failure_report(error)
50 summary = message if exception_type == "RuntimeError" else f"{exception_type}: {message}"
51 raise CollectiveConvergenceError(f"{failure_prefix}: {summary}") from error
54def failure_report(error: Exception, *, message: str | None = None) -> FailureReport:
55 """Build the picklable exception type and message for one local failure.
57 Args:
58 error: Local exception raised by the collective stage.
59 message: Optional user-facing replacement for the exception text.
60 """
61 return type(error).__name__, str(error) if message is None else message
64def format_collective_failures(
65 failures: list[FailureReport | None],
66 *,
67 rank_label: str = "rank",
68 ranks_label: str = "ranks",
69) -> str:
70 """Format only failed ranks, grouping peers with identical diagnostics.
72 Args:
73 failures: Failure report or ``None`` in collective-group rank order.
74 rank_label: Label for one failing rank.
75 ranks_label: Label for multiple ranks with the same failure.
77 Returns:
78 Compact failure descriptions in first-rank order.
79 """
80 grouped: dict[FailureReport, list[int]] = {}
81 for rank, failure in enumerate(failures):
82 if failure is not None:
83 grouped.setdefault(failure, []).append(rank)
84 descriptions = []
85 for (exception_type, message), ranks in grouped.items():
86 diagnostic = message if exception_type == "RuntimeError" else f"{exception_type}: {message}"
87 owner = f"{rank_label}={ranks[0]}" if len(ranks) == 1 else f"{ranks_label}={ranks}"
88 descriptions.append(f"{owner}: {diagnostic}")
89 return "; ".join(descriptions)
92def collective_call(
93 callback: Callable[[], None],
94 *,
95 stage: str,
96 rank_label: str = "rank",
97 ranks_label: str = "ranks",
98 group: Any,
99) -> None:
100 """Run one lifecycle stage and converge local failures across peers.
102 Args:
103 callback: Operation executed by every rank in the same order.
104 stage: User-facing description of that stage.
105 rank_label: Label for one failing collective-group rank.
106 ranks_label: Label for multiple ranks with the same failure.
107 group: Explicit collective group; None selects WORLD when initialized.
108 """
109 failure = None
110 try:
111 callback()
112 # Peers must see ordinary local errors before entering the next collective.
113 except Exception as error: # pylint: disable=broad-exception-caught
114 failure = error
115 report = None if failure is None else failure_report(failure)
116 failures = exchange(report, group=group, failure_prefix=f"{stage} failed while converging {rank_label} results")
117 if any(report is not None for report in failures):
118 details = format_collective_failures(failures, rank_label=rank_label, ranks_label=ranks_label)
119 raise RuntimeError(f"{stage} failed on {details}") from failure