Coverage for  / home / jenkins / .local / lib / python3.10 / site-packages / hyper_parallel / core / activation_checkpoint / recompute_state.py: 100%

49 statements  

« prev     ^ index     » next       coverage.py v7.13.1, created at 2026-08-04 05: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 

18 

19 

20class _RecomputeInvocation: 

21 """Own resources whose lifetime is one checkpoint invocation.""" 

22 

23 def __init__(self) -> None: 

24 """Initialize an empty invocation resource registry.""" 

25 self.identity = object() 

26 self.resources: Dict[object, Any] = {} 

27 

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] 

33 

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() 

41 

42 

43class RecomputeState: 

44 """Describe the current checkpoint invocation and execution phase.""" 

45 

46 def __init__(self, invocation: _RecomputeInvocation, recomputing: bool) -> None: 

47 """Initialize state for one phase of a checkpoint invocation.""" 

48 self._invocation = invocation 

49 self.is_recomputing = recomputing 

50 

51 @property 

52 def invocation_id(self) -> object: 

53 """Return an identity that is stable across forward and recomputation.""" 

54 return self._invocation.identity 

55 

56 def get_resource(self, key: object, factory: Callable[[], Any]) -> Any: 

57 """Get an invocation-local resource shared by both execution phases.""" 

58 return self._invocation.get_resource(key, factory) 

59 

60 def _clear_resources(self) -> None: 

61 """Release all resources owned by this checkpoint invocation.""" 

62 self._invocation.clear() 

63 

64 

65_CURRENT_RECOMPUTE_STATE: contextvars.ContextVar[Optional[RecomputeState]] = contextvars.ContextVar( 

66 "hyper_parallel_recompute_state", 

67 default=None, 

68) 

69 

70 

71class _RecomputeContext: 

72 """Install one checkpoint execution phase in the current dynamic scope.""" 

73 

74 def __init__(self, state: RecomputeState) -> None: 

75 """Initialize a context for the supplied execution state.""" 

76 self._state = state 

77 self._token = None 

78 

79 def __enter__(self) -> "_RecomputeContext": 

80 """Expose this phase as the current recompute state.""" 

81 self._token = _CURRENT_RECOMPUTE_STATE.set(self._state) 

82 return self 

83 

84 def __exit__(self, exc_type: Any, exc_value: Any, traceback: Any) -> bool: 

85 """Restore the outer state and clear resources after recomputation.""" 

86 if self._token is not None: 

87 _CURRENT_RECOMPUTE_STATE.reset(self._token) 

88 if self._state.is_recomputing or exc_type is not None: 

89 self._state._clear_resources() # pylint: disable=protected-access 

90 return False 

91 

92 

93def get_recompute_state() -> Optional[RecomputeState]: 

94 """Return the current checkpoint execution state, if one is active.""" 

95 return _CURRENT_RECOMPUTE_STATE.get() 

96 

97 

98def is_recomputing() -> bool: 

99 """Return whether the current dynamic scope is replaying a checkpoint.""" 

100 state = get_recompute_state() 

101 return state is not None and state.is_recomputing 

102 

103 

104def create_recompute_contexts() -> Tuple[_RecomputeContext, _RecomputeContext]: 

105 """Create forward and recompute contexts for one checkpoint invocation.""" 

106 invocation = _RecomputeInvocation() 

107 return ( 

108 _RecomputeContext(RecomputeState(invocation, recomputing=False)), 

109 _RecomputeContext(RecomputeState(invocation, recomputing=True)), 

110 )