ATLAS Offline Software
Toggle main menu visibility
Loading...
Searching...
No Matches
Control
AthenaConfiguration
python
AccumulatorCache.py
Go to the documentation of this file.
1
#
2
# Copyright (C) 2002-2026 CERN for the benefit of the ATLAS collaboration
3
#
4
5
from
AthenaCommon.Logging
import
logging
6
_msg = logging.getLogger(
'AccumulatorCache'
)
7
8
import
functools
9
import
time
10
from
abc
import
ABC, abstractmethod
11
from
copy
import
deepcopy
12
from
collections.abc
import
Hashable, Iterable
13
from
collections
import
defaultdict
14
from
dataclasses
import
dataclass
15
16
try
:
17
from
GaudiKernel.DataHandle
import
DataHandle
18
except
ImportError:
19
class
DataHandle
:
pass
# for analysis releases
20
21
22
def
make_hashable
(obj):
23
"""Make obj hashable by turning mutable lists into non-mutable tuples.
24
This is e.g. useful when kwargs is passed to a cached Cfg function and
25
contains non-hashable lists.
26
This transformation is usually safe because list-valued properties can
27
be set from a tuple or list.
28
29
Example: myCfg(**make_hashable(kwargs))
30
"""
31
if
isinstance(obj, list):
32
return
tuple(
make_hashable
(item)
for
item
in
obj)
33
elif
isinstance(obj, dict):
34
return
{key:
make_hashable
(value)
for
key, value
in
obj.items()}
35
elif
isinstance(obj, set):
36
return
{
make_hashable
(item)
for
item
in
obj}
37
return
obj
38
39
40
class
NotHashable
(Exception):
41
"""Exception thrown when AccumulatorCache is applied to non-hashable function call"""
42
def
__init__
(self, value):
43
super().__init__ (self)
44
self.
value
= value
45
46
47
class
AccumulatorCachable
(ABC):
48
"""Abstract base for classes needing custom AccumulatorCache behavior."""
49
50
@abstractmethod
51
def
_cacheEvict
(self):
52
"""This method is called by AccumulatorCache when an object is removed
53
from the cache. Implement this for custom cleanup actions."""
54
pass
55
56
57
class
AccumulatorDecorator:
58
"""Class for use in function decorators, implements memoization.
59
60
Instances are callable objects that use the
61
hash value calculated from positional and keyword arguments
62
to implement memoization. Methods for suspending and
63
resuming memoization are provided.
64
"""
65
66
_memoize =
True
67
68
VERIFY_NOTHING = 0
69
VERIFY_HASH = 1
70
71
@dataclass
72
class
CacheStats
:
73
hits : int = 0
74
misses: int = 0
75
t_hits: float = 0
76
t_misses: float = 0
77
78
_stats = defaultdict(CacheStats)
79
80
def
__init__
(self, func, size, verify, deepCopy):
81
"""See AccumulatorCache decorator for documentation of arguments."""
82
83
functools.update_wrapper(self , func)
84
self.
_maxSize
= size
85
self.
_func
= func
86
self.
_cache
= {}
87
self.
_resultCache
= {}
88
self.
_verify
= verify
89
self.
_deepcopy
= deepCopy
90
91
if
self.
_verify
not
in
[self.
VERIFY_NOTHING
, self.
VERIFY_HASH
]:
92
raise
RuntimeError(f
"Invalid value for verify ({verify}) in AccumulatorCache for {func}"
)
93
94
def
getInfo
(self):
95
"""Return a dictionary with information about the cache size and cache usage"""
96
return
{
"cache_size"
: len(self.
_cache
),
97
"misses"
: self.
_stats
[self].misses,
98
"hits"
: self.
_stats
[self].hits,
99
"function"
: self.
_func
,
100
"result_cache_size"
: len(self.
_resultCache
)}
101
102
@classmethod
103
def
printStats
(cls):
104
"""Print cache statistics"""
105
header =
"%-70s | Hits (time) | Misses (time) |"
%
"AccumulatorCache"
106
print
(
"-"
*len(header))
107
print
(header)
108
print
(
"-"
*len(header))
109
# Print sorted by hit+miss time:
110
for
func, stats
in
sorted(cls.
_stats
.items(), key=
lambda
s:s[1].t_hits+s[1].t_misses, reverse=
True
):
111
name = f
"{func.__module__}.{func.__name__}"
112
if
len(name) > 70:
113
name =
'...'
+ name[-67:]
114
print
(f
"{name:70} | {stats.hits:6} ({stats.t_hits:4.1f}s) | "
115
f
"{stats.misses:6} ({stats.t_misses:4.1f}s) |"
)
116
print
(
"-"
*len(header))
117
118
@classmethod
119
def
suspendCaching
(cls):
120
"""Suspend memoization for all instances of AccumulatorDecorator."""
121
cls.
_memoize
=
False
122
123
@classmethod
124
def
resumeCaching
(cls):
125
"""Resume memoization for all instances of AccumulatorDecorator."""
126
cls.
_memoize
=
True
127
128
@classmethod
129
def
clearCache
(cls):
130
"""Clear all accumulator caches"""
131
for
decor
in
cls.
_stats
:
132
decor._evictAll()
133
decor._cache.clear()
134
decor._resultCache.clear()
135
136
cls.
_stats
.
clear
()
137
138
def
_getHash
(x):
139
if
hasattr(x,
"athHash"
):
140
return
x.athHash()
141
elif
isinstance(x, Hashable):
142
return
hash(x)
143
elif
isinstance(x, DataHandle):
144
return
hash(repr(x))
145
raise
NotHashable
(x)
146
147
def
_evict
(x):
148
"""Called when x is removed from the cache"""
149
if
isinstance(x, AccumulatorCachable):
150
x._cacheEvict()
151
elif
isinstance(x, Iterable)
and
not
isinstance(x, str):
152
for
el
in
x:
153
AccumulatorDecorator._evict(el)
154
155
def
_evictAll
(self):
156
for
v
in
self.
_cache
.values():
157
AccumulatorDecorator._evict(v)
158
159
def
__get__
(self, obj, objtype):
160
"""Support instance methods."""
161
return
functools.partial(self.
__call__
, obj)
162
163
def
__call__
(self, *args, **kwargs):
164
cacheHit =
None
165
try
:
166
t0 = time.perf_counter()
167
res, cacheHit = self.
_callImpl
(*args, **kwargs)
168
return
res
169
except
NotHashable
as
e:
170
_msg.warning(f
"Argument value '{repr(e.value)}' in {self._func} is not hashable. "
171
"No caching is performed!"
)
172
cacheHit =
False
173
return
self.
_func
(*args, **kwargs)
# perform regular function call
174
finally
:
175
t1 = time.perf_counter()
176
if
cacheHit
is
True
:
177
self.
_stats
[self].hits += 1
178
self.
_stats
[self].t_hits += (t1-t0)
179
elif
cacheHit
is
False
:
180
self.
_stats
[self].misses += 1
181
self.
_stats
[self].t_misses += (t1-t0)
182
183
def
_callImpl
(self, *args, **kwargs):
184
"""Implementation of __call__.
185
186
Returns: (result, cacheHit)
187
"""
188
189
# AccumulatorCache enabled?
190
if
not
AccumulatorDecorator._memoize:
191
return
(self.
_func
(*args , **kwargs),
None
)
192
193
# frozen set makes the order of keyword arguments irrelevant
194
hsh = hash( (tuple(AccumulatorDecorator._getHash(a)
for
a
in
args),
195
frozenset((hash(k), AccumulatorDecorator._getHash(v))
for
k,v
in
kwargs.items())) )
196
197
res = self.
_cache
.
get
(hsh,
None
)
198
if
res
is
not
None
:
199
cacheHit =
None
200
if
AccumulatorDecorator.VERIFY_HASH == self.
_verify
:
201
resHsh = self.
_resultCache
[hsh]
202
chkHsh = AccumulatorDecorator._getHash(res)
203
if
chkHsh != resHsh:
204
_msg.debug(
"Hash of function result, cached using AccumulatorDecorator, changed."
)
205
cacheHit =
False
206
res = self.
_func
(*args , **kwargs)
207
self.
_cache
[hsh] = res
208
self.
_resultCache
[hsh] = AccumulatorDecorator._getHash(res)
209
else
:
210
cacheHit =
True
211
else
:
212
cacheHit =
True
213
214
if
self.
_deepcopy
:
215
return
deepcopy(res), cacheHit
216
else
:
217
# shallow copied CA still needs to undergo merging
218
from
AthenaConfiguration.ComponentAccumulator
import
ComponentAccumulator
219
if
isinstance(res, ComponentAccumulator):
220
res._wasMerged=
False
221
return
res, cacheHit
222
223
else
:
224
_msg.debug(
'Hash not found in AccumulatorCache for function %s'
, self.
_func
)
225
if
len(self.
_cache
) >= self.
_maxSize
:
226
_msg.warning(
"Cache limit (%d) reached for %s.%s"
,
227
self.
_maxSize
, self.
_func
.__module__, self.
_func
.__name__)
228
oldest = self.
_cache
.pop(next(iter(self.
_cache
)))
229
AccumulatorDecorator._evict(oldest)
230
231
res = self.
_func
(*args , **kwargs)
232
233
if
AccumulatorDecorator.VERIFY_HASH == self.
_verify
:
234
if
len(self.
_resultCache
) >= self.
_maxSize
:
235
del self.
_resultCache
[next(iter(self.
_resultCache
))]
236
self.
_resultCache
[hsh] = AccumulatorDecorator._getHash(res)
237
self.
_cache
[hsh] = res
238
else
:
239
self.
_cache
[hsh] = res
240
241
return
(deepcopy(res)
if
self.
_deepcopy
else
res,
False
)
242
243
def
__del__
(self):
244
self.
_evictAll
()
245
246
247
def
AccumulatorCache(func = None, maxSize = 128,
248
verifyResult = AccumulatorDecorator.VERIFY_NOTHING, deepCopy = True):
249
"""Function decorator, implements memoization.
250
251
Keyword arguments:
252
maxSize: maximum size for the cache associated with the function (default 128)
253
verifyResult: takes two possible values
254
255
AccumulatorDecorator.VERIFY_NOTHING - default, the cached function result is returned with no verification
256
AccumulatorDecorator.VERIFY_HASH - before returning the cached function value, the hash of the
257
result is checked to verify if this object was not modified
258
between function calls
259
deepCopy: if True (default) a deep copy of the function result will be stored in the cache.
260
261
Returns:
262
An instance of AccumulatorDecorator.
263
"""
264
265
def
wrapper_accumulator(func):
266
return
AccumulatorDecorator
(func, maxSize, verifyResult, deepCopy)
267
268
return
wrapper_accumulator(func)
if
func
else
wrapper_accumulator
clear
void clear()
Empty the pool.
print
void print(char *figname, TCanvas *c1)
Definition
TRTCalib_StrawStatusPlots.cxx:26
python.AccumulatorCache.AccumulatorCachable
Definition
AccumulatorCache.py:47
python.AccumulatorCache.AccumulatorCachable._cacheEvict
_cacheEvict(self)
Definition
AccumulatorCache.py:51
python.AccumulatorCache.AccumulatorDecorator.CacheStats
Definition
AccumulatorCache.py:72
python.AccumulatorCache.AccumulatorDecorator
Definition
AccumulatorCache.py:57
python.AccumulatorCache.AccumulatorDecorator._memoize
bool _memoize
Definition
AccumulatorCache.py:66
python.AccumulatorCache.AccumulatorDecorator.suspendCaching
suspendCaching(cls)
Definition
AccumulatorCache.py:119
python.AccumulatorCache.AccumulatorDecorator._resultCache
dict _resultCache
Definition
AccumulatorCache.py:87
python.AccumulatorCache.AccumulatorDecorator.VERIFY_NOTHING
int VERIFY_NOTHING
Definition
AccumulatorCache.py:68
python.AccumulatorCache.AccumulatorDecorator.__init__
__init__(self, func, size, verify, deepCopy)
Definition
AccumulatorCache.py:80
python.AccumulatorCache.AccumulatorDecorator._getHash
_getHash(x)
Definition
AccumulatorCache.py:138
python.AccumulatorCache.AccumulatorDecorator._maxSize
_maxSize
Definition
AccumulatorCache.py:84
python.AccumulatorCache.AccumulatorDecorator._cache
dict _cache
Definition
AccumulatorCache.py:86
python.AccumulatorCache.AccumulatorDecorator.__del__
__del__(self)
Definition
AccumulatorCache.py:243
python.AccumulatorCache.AccumulatorDecorator.resumeCaching
resumeCaching(cls)
Definition
AccumulatorCache.py:124
python.AccumulatorCache.AccumulatorDecorator.VERIFY_HASH
int VERIFY_HASH
Definition
AccumulatorCache.py:69
python.AccumulatorCache.AccumulatorDecorator._evict
_evict(x)
Definition
AccumulatorCache.py:147
python.AccumulatorCache.AccumulatorDecorator._verify
_verify
Definition
AccumulatorCache.py:88
python.AccumulatorCache.AccumulatorDecorator.clearCache
clearCache(cls)
Definition
AccumulatorCache.py:129
python.AccumulatorCache.AccumulatorDecorator._deepcopy
_deepcopy
Definition
AccumulatorCache.py:89
python.AccumulatorCache.AccumulatorDecorator._callImpl
_callImpl(self, *args, **kwargs)
Definition
AccumulatorCache.py:183
python.AccumulatorCache.AccumulatorDecorator.__get__
__get__(self, obj, objtype)
Definition
AccumulatorCache.py:159
python.AccumulatorCache.AccumulatorDecorator.__call__
__call__
Definition
AccumulatorCache.py:161
python.AccumulatorCache.AccumulatorDecorator._evictAll
_evictAll(self)
Definition
AccumulatorCache.py:155
python.AccumulatorCache.AccumulatorDecorator.printStats
printStats(cls)
Definition
AccumulatorCache.py:103
python.AccumulatorCache.AccumulatorDecorator._func
_func
Definition
AccumulatorCache.py:85
python.AccumulatorCache.AccumulatorDecorator.getInfo
getInfo(self)
Definition
AccumulatorCache.py:94
python.AccumulatorCache.AccumulatorDecorator._stats
_stats
Definition
AccumulatorCache.py:78
python.AccumulatorCache.DataHandle
Definition
AccumulatorCache.py:19
python.AccumulatorCache.NotHashable
Definition
AccumulatorCache.py:40
python.AccumulatorCache.NotHashable.value
value
Definition
AccumulatorCache.py:44
python.AccumulatorCache.NotHashable.__init__
__init__(self, value)
Definition
AccumulatorCache.py:42
get
T * get(TKey *tobj)
get a TObject* from a TKey* (why can't a TObject be a TKey?)
Definition
hcg.cxx:132
collections.abc
python.AccumulatorCache.make_hashable
make_hashable(obj)
Definition
AccumulatorCache.py:22
Generated on
for ATLAS Offline Software by
1.17.0