Coverage for / home / jenkins / .local / lib / python3.10 / site-packages / hyper_parallel / auto_parallel / hyper_offload / api / session.py: 100%
52 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 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"""Activation offload session built on TorchDispatchMode."""
17from __future__ import annotations
19import contextvars
20import logging
22from torch.utils._python_dispatch import TorchDispatchMode
24from hyper_parallel.auto_parallel.hyper_offload.api.config import OffloadConfig
25from hyper_parallel.auto_parallel.hyper_offload.execution.replay import ReplayExecutor
26from hyper_parallel.auto_parallel.hyper_offload.execution.warmup import WarmupExecutor
27from hyper_parallel.auto_parallel.hyper_offload.planning.greedy import GreedyResidencyPlanner
28from hyper_parallel.auto_parallel.hyper_offload.runtime.residency import ResidencyManager
30logger = logging.getLogger(__name__)
32# Context variable tracking the active OffloadSession (set on __enter__).
33_active_session: contextvars.ContextVar[OffloadSession | None] = contextvars.ContextVar("_active_session", default=None)
36class ActivationDispatchMode(TorchDispatchMode):
37 """Dispatch mode that delegates to a :class:`BaseExecutor`.
39 The executor (warmup or replay) is selected by
40 :class:`OffloadSession` and never swapped while this mode
41 is active.
42 """
44 def __init__(self, session: OffloadSession):
45 super().__init__()
46 self.session = session
48 def __torch_dispatch__(self, func, types, args=(), kwargs=None): # pylint: disable=unused-argument
49 """Dispatch a torch operation."""
50 return self.session.executor.dispatch(func, args, kwargs)
53class OffloadSession:
54 """Manage warmup and replay for activation residency control.
56 Holds lifecycle state and a single active executor (:class:`WarmupExecutor`
57 or :class:`ReplayExecutor`). The executor is switched during the
58 warmup → replay transition in :meth:`__exit__`.
59 """
61 def __init__(self, config: OffloadConfig):
62 self.mode: str = "warmup"
64 self.planner = config.planner or GreedyResidencyPlanner()
65 self.residency_manager = ResidencyManager(
66 max_host_bytes=config.max_offload_activation_mb * 1024**2,
67 )
68 self.executor: WarmupExecutor | ReplayExecutor = WarmupExecutor(
69 residency_manager=self.residency_manager,
70 memory_limit_bytes=config.max_resident_activation_mb * 1024**2,
71 )
73 self._dispatch_context = None
74 self._session_token = None
76 # ------------------------------------------------------------------
77 # Active session lookup (used by skip_offload decorator)
78 # ------------------------------------------------------------------
80 @staticmethod
81 def get_active() -> OffloadSession | None:
82 """Return the currently active OffloadSession, or ``None``."""
83 return _active_session.get()
85 # ------------------------------------------------------------------
86 # Context manager
87 # ------------------------------------------------------------------
89 def __enter__(self):
90 """Enter the runtime context."""
91 self.executor.reset()
93 self._session_token = _active_session.set(self)
94 self._dispatch_context = ActivationDispatchMode(self)
95 self._dispatch_context.__enter__()
96 return self
98 def __exit__(self, exc_type, exc_val, exc_tb):
99 """Exit the runtime context."""
100 if self._dispatch_context is not None:
101 self._dispatch_context.__exit__(exc_type, exc_val, exc_tb)
102 self._dispatch_context = None
103 if self._session_token is not None:
104 _active_session.reset(self._session_token)
105 self._session_token = None
107 if exc_type is not None:
108 self.residency_manager.sync_all_transfers()
109 self.executor.reset()
110 return False
112 self.residency_manager.wait_for_transfers()
114 if self.mode == "warmup":
115 trace, guide = self.executor.finish()
116 schedule = self.planner.build(trace)
117 self.executor = ReplayExecutor(self.residency_manager, schedule, guide)
118 self.mode = "replay"
120 return False