Coverage for / home / jenkins / .local / lib / python3.10 / site-packages / hyper_parallel / core / activation_memory / recompute_state.py: 100%
50 statements
« prev ^ index » next coverage.py v7.13.1, created at 2026-11-15 07:18 +0800
« prev ^ index » next coverage.py v7.13.1, created at 2026-11-15 07:18 +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"""Dynamic execution state shared by activation recomputation features."""
16import contextvars
17from typing import Any, Callable, Dict, Optional, Tuple
20class _RecomputeInvocation:
21 """Own resources whose lifetime is one checkpoint invocation."""
23 def __init__(self) -> None:
24 """Initialize an empty invocation resource registry."""
25 self.identity = object()
26 self.resources: Dict[object, Any] = {}
28 def get_resource(self, key: object, factory: Callable[[], Any]) -> Any:
29 """Get or create one invocation-local resource."""
30 if key not in self.resources:
31 self.resources[key] = factory()
32 return self.resources[key]
34 def clear(self) -> None:
35 """Release invocation resources, including partially consumed caches."""
36 for resource in self.resources.values():
37 clear = getattr(resource, "clear", None)
38 if clear is not None:
39 clear()
40 self.resources.clear()
43class RecomputeState:
44 """Describe the current checkpoint invocation and execution phase."""
46 def __init__(
47 self,
48 invocation: _RecomputeInvocation,
49 recomputing: bool,
50 preserve_rng_state: bool = True,
51 ) -> None:
52 """Initialize state for one phase of a checkpoint invocation."""
53 self._invocation = invocation
54 self.is_recomputing = recomputing
55 self.preserve_rng_state = preserve_rng_state
57 @property
58 def invocation_id(self) -> object:
59 """Return an identity that is stable across forward and recomputation."""
60 return self._invocation.identity
62 def get_resource(self, key: object, factory: Callable[[], Any]) -> Any:
63 """Get an invocation-local resource shared by both execution phases."""
64 return self._invocation.get_resource(key, factory)
66 def _clear_resources(self) -> None:
67 """Release all resources owned by this checkpoint invocation."""
68 self._invocation.clear()
71_CURRENT_RECOMPUTE_STATE: contextvars.ContextVar[Optional[RecomputeState]] = contextvars.ContextVar(
72 "hyper_parallel_recompute_state",
73 default=None,
74)
77class _RecomputeContext:
78 """Install one checkpoint execution phase in the current dynamic scope."""
80 def __init__(self, state: RecomputeState) -> None:
81 """Initialize a context for the supplied execution state."""
82 self._state = state
83 self._token = None
85 def __enter__(self) -> "_RecomputeContext":
86 """Expose this phase as the current recompute state."""
87 self._token = _CURRENT_RECOMPUTE_STATE.set(self._state)
88 return self
90 def __exit__(self, exc_type: Any, exc_value: Any, traceback: Any) -> bool:
91 """Restore the outer state and clear resources after recomputation."""
92 if self._token is not None:
93 _CURRENT_RECOMPUTE_STATE.reset(self._token)
94 if self._state.is_recomputing or exc_type is not None:
95 self._state._clear_resources() # pylint: disable=protected-access
96 return False
99def get_recompute_state() -> Optional[RecomputeState]:
100 """Return the current checkpoint execution state, if one is active."""
101 return _CURRENT_RECOMPUTE_STATE.get()
104def is_recomputing() -> bool:
105 """Return whether the current dynamic scope is replaying a checkpoint."""
106 state = get_recompute_state()
107 return state is not None and state.is_recomputing
110def create_recompute_contexts(
111 preserve_rng_state: bool = True,
112) -> Tuple[_RecomputeContext, _RecomputeContext]:
113 """Create forward and recompute contexts for one checkpoint invocation."""
114 invocation = _RecomputeInvocation()
115 return (
116 _RecomputeContext(
117 RecomputeState(invocation, recomputing=False, preserve_rng_state=preserve_rng_state)
118 ),
119 _RecomputeContext(
120 RecomputeState(invocation, recomputing=True, preserve_rng_state=preserve_rng_state)
121 ),
122 )