root / ase / utils / memory.py @ 19
Historique | Voir | Annoter | Télécharger (16,32 ko)
1 | 1 | tkerber | import os |
---|---|---|---|
2 | 1 | tkerber | import numpy as np |
3 | 1 | tkerber | |
4 | 1 | tkerber | from UserDict import DictMixin |
5 | 1 | tkerber | |
6 | 1 | tkerber | # -------------------------------------------------------------------
|
7 | 1 | tkerber | |
8 | 1 | tkerber | class MemoryBase(object, DictMixin): |
9 | 1 | tkerber | """Virtual memory (VM) statistics of the current process
|
10 | 1 | tkerber | obtained from the relevant entries in /proc/<pid>/status:
|
11 | 1 | tkerber | VmPeak Peak virtual memory size in bytes.
|
12 | 1 | tkerber | VmLck ???
|
13 | 1 | tkerber | VmHWM Peak resident set size ("high water mark") in bytes.
|
14 | 1 | tkerber | VmRSS Resident memory usage in bytes.
|
15 | 1 | tkerber | VmSize VM usage of the entire process in bytes.
|
16 | 1 | tkerber | VmData VM usage of heap in bytes.
|
17 | 1 | tkerber | VmStk VM usage of stack in bytes.
|
18 | 1 | tkerber | VmExe VM usage of exe's and statically linked libraries in bytes.
|
19 | 1 | tkerber | VmLib VM usage of dynamically linked libraries in bytes.
|
20 | 1 | tkerber | VmPTE ???
|
21 | 1 | tkerber |
|
22 | 1 | tkerber | Note that VmSize > VmData + VmStk + VmExe + VmLib due to overhead.
|
23 | 1 | tkerber | """
|
24 | 1 | tkerber | |
25 | 1 | tkerber | _scale = {'KB':1024.0, 'MB':1024.0**2} |
26 | 1 | tkerber | _keys = ('VmPeak', 'VmLck', 'VmHWM', 'VmRSS', 'VmSize', 'VmData', \ |
27 | 1 | tkerber | 'VmStk', 'VmExe', 'VmLib', 'VmPTE') |
28 | 1 | tkerber | |
29 | 1 | tkerber | def __init__(self, verbose=0): |
30 | 1 | tkerber | self.verbose = verbose
|
31 | 1 | tkerber | if self.verbose>=2: print 'MemoryBase.__init__' |
32 | 1 | tkerber | object.__init__(self) |
33 | 1 | tkerber | self._values = np.empty(len(self._keys), dtype=np.float) |
34 | 1 | tkerber | |
35 | 1 | tkerber | def __repr__(self): |
36 | 1 | tkerber | """Return a representation of recorded VM statistics.
|
37 | 1 | tkerber | x.__repr__() <==> repr(x)"""
|
38 | 1 | tkerber | if self.verbose>=2: print 'MemoryBase.__repr__' |
39 | 1 | tkerber | s = object.__repr__(self) |
40 | 1 | tkerber | w = max(map(len, self._keys)) |
41 | 1 | tkerber | unit = 'MB'
|
42 | 1 | tkerber | for k,v in self.iteritems(): |
43 | 1 | tkerber | res = '<N/A>'
|
44 | 1 | tkerber | if not np.isnan(v): |
45 | 1 | tkerber | res = '%8.3f %s' % (v/self._scale[unit], unit) |
46 | 1 | tkerber | s += '\n\t' + k.ljust(w) + ': ' + res.rjust(8) |
47 | 1 | tkerber | return s
|
48 | 1 | tkerber | |
49 | 1 | tkerber | def __len__(self): |
50 | 1 | tkerber | """Number of VM keys which have not been outdated.
|
51 | 1 | tkerber | x.__len__() <==> len(x)"""
|
52 | 1 | tkerber | if self.verbose>=3: print 'MemoryBase.__len__' |
53 | 1 | tkerber | return np.sum(~np.isnan(self._values)) |
54 | 1 | tkerber | |
55 | 1 | tkerber | def __getitem__(self, key): |
56 | 1 | tkerber | """Return floating point number associated with a VM key.
|
57 | 1 | tkerber | x.__getitem__(y) <==> x[y]"""
|
58 | 1 | tkerber | if self.verbose>=2: print 'MemoryBase.__getitem__' |
59 | 1 | tkerber | if key not in self: |
60 | 1 | tkerber | raise KeyError(key) |
61 | 1 | tkerber | i = self.keys().index(key)
|
62 | 1 | tkerber | return self._values[i] |
63 | 1 | tkerber | |
64 | 1 | tkerber | def __setitem__(self, key, value): |
65 | 1 | tkerber | """x.__setitem__(i, y) <==> x[i]=y"""
|
66 | 1 | tkerber | if self.verbose>=2: print 'MemoryBase.__setitem__' |
67 | 1 | tkerber | raise Exception('Virtual member function.') |
68 | 1 | tkerber | |
69 | 1 | tkerber | def __delitem__(self, key): |
70 | 1 | tkerber | """x.__delitem__(y) <==> del x[y]"""
|
71 | 1 | tkerber | if self.verbose>=2: print 'MemoryBase.__delitem__' |
72 | 1 | tkerber | raise Exception('Virtual member function.') |
73 | 1 | tkerber | |
74 | 1 | tkerber | def clear(self): |
75 | 1 | tkerber | """D.clear() -> None. Remove all items from D."""
|
76 | 1 | tkerber | if self.verbose>=1: print 'MemoryBase.clear' |
77 | 1 | tkerber | raise Exception('Virtual member function.') |
78 | 1 | tkerber | |
79 | 1 | tkerber | def update(self, other=None): |
80 | 1 | tkerber | """D.update(E) -> None. Update D from E: for k in E.keys(): D[k] = E[k]"""
|
81 | 1 | tkerber | if self.verbose>=1: print 'MemoryBase.update' |
82 | 1 | tkerber | DictMixin.update(self, other)
|
83 | 1 | tkerber | |
84 | 1 | tkerber | def copy(self): |
85 | 1 | tkerber | """Return a shallow copy of a VM statistics instance.
|
86 | 1 | tkerber | D.copy() -> a shallow copy of D"""
|
87 | 1 | tkerber | if self.verbose>=1: print 'MemoryBase.copy' |
88 | 1 | tkerber | res = object.__new__(self.__class__) |
89 | 1 | tkerber | MemoryBase.__init__(res, self.verbose)
|
90 | 1 | tkerber | DictMixin.update(res, self)
|
91 | 1 | tkerber | return res
|
92 | 1 | tkerber | |
93 | 1 | tkerber | def has_key(self, key): #necessary to avoid infinite recursion |
94 | 1 | tkerber | """Return boolean to indicate whether key is a supported VM key.
|
95 | 1 | tkerber | D.has_key(k) -> True if D has a key k, else False"""
|
96 | 1 | tkerber | if self.verbose>=3: print 'MemoryBase.has_key' |
97 | 1 | tkerber | return key in self._keys |
98 | 1 | tkerber | |
99 | 1 | tkerber | def keys(self): |
100 | 1 | tkerber | """Return list of supported VM keys.
|
101 | 1 | tkerber | D.keys() -> list of D's keys"""
|
102 | 1 | tkerber | if self.verbose>=3: print 'MemoryBase.keys' |
103 | 1 | tkerber | return list(self._keys) |
104 | 1 | tkerber | |
105 | 1 | tkerber | def values(self): |
106 | 1 | tkerber | """Return list of recorded VM statistics.
|
107 | 1 | tkerber | D.values() -> list of D's values"""
|
108 | 1 | tkerber | if self.verbose>=3: print 'MemoryBase.values' |
109 | 1 | tkerber | return list(self._values) |
110 | 1 | tkerber | |
111 | 1 | tkerber | def get(self, key, default=None): |
112 | 1 | tkerber | """Return floating point number associated with a VM key.
|
113 | 1 | tkerber | D.get(k[,d]) -> D[k] if k in D, else d. d defaults to None."""
|
114 | 1 | tkerber | if self.verbose>=1: print 'MemoryBase.get' |
115 | 1 | tkerber | v = self[key]
|
116 | 1 | tkerber | |
117 | 1 | tkerber | if type(default) in [int,float]: |
118 | 1 | tkerber | default = np.float_(default) |
119 | 1 | tkerber | if default is not None and not isinstance(default, np.floating): |
120 | 1 | tkerber | raise ValueError('Default value must be a floating point number.') |
121 | 1 | tkerber | |
122 | 1 | tkerber | if default is not None and np.isnan(v): |
123 | 1 | tkerber | return default
|
124 | 1 | tkerber | else:
|
125 | 1 | tkerber | return v
|
126 | 1 | tkerber | |
127 | 1 | tkerber | def setdefault(self, key, default=None): |
128 | 1 | tkerber | """Return floating point number associated with a VM key.
|
129 | 1 | tkerber | D.setdefault(k[,d]) -> D.get(k,d), also set D[k]=d if k not in D"""
|
130 | 1 | tkerber | if self.verbose>=1: print 'MemoryBase.setdefault' |
131 | 1 | tkerber | v = self[key]
|
132 | 1 | tkerber | |
133 | 1 | tkerber | if type(default) in [int,float]: |
134 | 1 | tkerber | default = np.float_(default) |
135 | 1 | tkerber | if default is not None and not isinstance(default, np.floating): |
136 | 1 | tkerber | raise ValueError('Default value must be a floating point number.') |
137 | 1 | tkerber | |
138 | 1 | tkerber | if default is not None and np.isnan(v): |
139 | 1 | tkerber | self[key] = default
|
140 | 1 | tkerber | return default
|
141 | 1 | tkerber | else:
|
142 | 1 | tkerber | return v
|
143 | 1 | tkerber | |
144 | 1 | tkerber | def pop(self, key, default=None): |
145 | 1 | tkerber | """Return floating point number for a VM key and mark it as outdated.
|
146 | 1 | tkerber | D.pop(k[,d]) -> v, remove specified key and return the corresponding value
|
147 | 1 | tkerber | If key is not found, d is returned if given, otherwise KeyError is raised"""
|
148 | 1 | tkerber | |
149 | 1 | tkerber | if self.verbose>=1: print 'MemoryBase.pop' |
150 | 1 | tkerber | v = self[key]
|
151 | 1 | tkerber | |
152 | 1 | tkerber | if type(default) in [int,float]: |
153 | 1 | tkerber | default = np.float_(default) |
154 | 1 | tkerber | if default is not None and not isinstance(default, np.floating): |
155 | 1 | tkerber | raise ValueError('Default value must be a floating point number.') |
156 | 1 | tkerber | |
157 | 1 | tkerber | if default is not None and np.isnan(v): |
158 | 1 | tkerber | return default
|
159 | 1 | tkerber | else:
|
160 | 1 | tkerber | del self[key] |
161 | 1 | tkerber | return v
|
162 | 1 | tkerber | |
163 | 1 | tkerber | def popitem(self): |
164 | 1 | tkerber | """Return floating point number for some not-yet outdated VM key.
|
165 | 1 | tkerber | D.popitem() -> (k, v), remove and return some (key, value) pair as a
|
166 | 1 | tkerber | 2-tuple; but raise KeyError if D is empty"""
|
167 | 1 | tkerber | if self.verbose>=1: print 'MemoryBase.popitem' |
168 | 1 | tkerber | |
169 | 1 | tkerber | for k,v in self.iteritems(): |
170 | 1 | tkerber | if not np.isnan(v): |
171 | 1 | tkerber | del self[k] |
172 | 1 | tkerber | return (k,v)
|
173 | 1 | tkerber | raise KeyError |
174 | 1 | tkerber | |
175 | 1 | tkerber | def __add__(self, other): |
176 | 1 | tkerber | """x.__add__(y) <==> x+y"""
|
177 | 1 | tkerber | if self.verbose>=1: print 'MemoryBase.__add__(%s,%s)' \ |
178 | 1 | tkerber | % (object.__repr__(self), object.__repr__(other)) |
179 | 1 | tkerber | res = self.copy()
|
180 | 1 | tkerber | if isinstance(other, MemoryBase): |
181 | 1 | tkerber | res._values.__iadd__(other._values) |
182 | 1 | tkerber | elif type(other) in [int,float]: |
183 | 1 | tkerber | res._values.__iadd__(other) |
184 | 1 | tkerber | else:
|
185 | 1 | tkerber | raise TypeError('Unsupported operand type') |
186 | 1 | tkerber | return res
|
187 | 1 | tkerber | |
188 | 1 | tkerber | def __sub__(self, other): |
189 | 1 | tkerber | """x.__sub__(y) <==> x-y"""
|
190 | 1 | tkerber | if self.verbose>=1: print 'MemoryBase.__sub__(%s,%s)' \ |
191 | 1 | tkerber | % (object.__repr__(self), object.__repr__(other)) |
192 | 1 | tkerber | res = self.copy()
|
193 | 1 | tkerber | if isinstance(other, MemoryBase): |
194 | 1 | tkerber | res._values.__isub__(other._values) |
195 | 1 | tkerber | elif type(other) in [int,float]: |
196 | 1 | tkerber | res._values.__isub__(other) |
197 | 1 | tkerber | else:
|
198 | 1 | tkerber | raise TypeError('Unsupported operand type') |
199 | 1 | tkerber | return res
|
200 | 1 | tkerber | |
201 | 1 | tkerber | def __radd__(self, other): |
202 | 1 | tkerber | """x.__radd__(y) <==> y+x"""
|
203 | 1 | tkerber | if self.verbose>=1: print 'MemoryBase.__radd__(%s,%s)' \ |
204 | 1 | tkerber | % (object.__repr__(self), object.__repr__(other)) |
205 | 1 | tkerber | res = self.copy()
|
206 | 1 | tkerber | if isinstance(other, MemoryBase): |
207 | 1 | tkerber | res._values.__iadd__(other._values) |
208 | 1 | tkerber | elif type(other) in [int,float]: |
209 | 1 | tkerber | res._values.__iadd__(other) |
210 | 1 | tkerber | else:
|
211 | 1 | tkerber | raise TypeError('Unsupported operand type') |
212 | 1 | tkerber | return res
|
213 | 1 | tkerber | |
214 | 1 | tkerber | def __rsub__(self, other): |
215 | 1 | tkerber | """x.__rsub__(y) <==> y-x"""
|
216 | 1 | tkerber | if self.verbose>=1: print 'MemoryBase.__rsub__(%s,%s)' \ |
217 | 1 | tkerber | % (object.__repr__(self), object.__repr__(other)) |
218 | 1 | tkerber | res = self.copy()
|
219 | 1 | tkerber | res._values.__imul__(-1.0)
|
220 | 1 | tkerber | if isinstance(other, MemoryBase): |
221 | 1 | tkerber | res._values.__iadd__(other._values) |
222 | 1 | tkerber | elif type(other) in [int,float]: |
223 | 1 | tkerber | res._values.__iadd__(other) |
224 | 1 | tkerber | else:
|
225 | 1 | tkerber | raise TypeError('Unsupported operand type') |
226 | 1 | tkerber | return res
|
227 | 1 | tkerber | |
228 | 1 | tkerber | # -------------------------------------------------------------------
|
229 | 1 | tkerber | |
230 | 1 | tkerber | class MemoryStatistics(MemoryBase): |
231 | 1 | tkerber | |
232 | 1 | tkerber | def __init__(self, verbose=0): |
233 | 1 | tkerber | MemoryBase.__init__(self, verbose)
|
234 | 1 | tkerber | self.update()
|
235 | 1 | tkerber | |
236 | 1 | tkerber | def __setitem__(self, key, value): |
237 | 1 | tkerber | """Set VM key to a floating point number.
|
238 | 1 | tkerber | x.__setitem__(i, y) <==> x[i]=y"""
|
239 | 1 | tkerber | if self.verbose>=2: print 'MemoryStatistics.__setitem__' |
240 | 1 | tkerber | if key not in self: |
241 | 1 | tkerber | raise KeyError(key) |
242 | 1 | tkerber | if type(value) in [int,float]: |
243 | 1 | tkerber | value = np.float_(value) |
244 | 1 | tkerber | if not isinstance(value, np.floating): |
245 | 1 | tkerber | raise ValueError('Value must be a floating point number.') |
246 | 1 | tkerber | i = self.keys().index(key)
|
247 | 1 | tkerber | self._values[i] = value
|
248 | 1 | tkerber | |
249 | 1 | tkerber | def __delitem__(self, key): |
250 | 1 | tkerber | """Mark a VK key as outdated.
|
251 | 1 | tkerber | x.__delitem__(y) <==> del x[y]"""
|
252 | 1 | tkerber | if self.verbose>=2: print 'MemoryStatistics.__delitem__' |
253 | 1 | tkerber | if key not in self: |
254 | 1 | tkerber | raise KeyError(key) |
255 | 1 | tkerber | self[key] = np.nan
|
256 | 1 | tkerber | |
257 | 1 | tkerber | def clear(self): |
258 | 1 | tkerber | """Mark all supported VM keys as outdated.
|
259 | 1 | tkerber | D.clear() -> None. Remove all items from D."""
|
260 | 1 | tkerber | if self.verbose>=1: print 'MemoryStatistics.clear' |
261 | 1 | tkerber | self._values[:] = np.nan
|
262 | 1 | tkerber | |
263 | 1 | tkerber | def refresh(self): |
264 | 1 | tkerber | """Refresh all outdated VM keys by reading /proc/<pid>/status."""
|
265 | 1 | tkerber | if self.verbose>=1: print 'MemoryBase.refresh' |
266 | 1 | tkerber | |
267 | 1 | tkerber | # NB: Linux /proc is for humans; Solaris /proc is for programs!
|
268 | 1 | tkerber | # TODO: Use pipe from 'prstat -p <pid>' or 'pmap -x <pid> 1 1'
|
269 | 1 | tkerber | |
270 | 1 | tkerber | # Skip refresh if none are outdated (i.e. nan)
|
271 | 1 | tkerber | if not np.isnan(self._values).any(): |
272 | 1 | tkerber | if self.verbose>=2: print 'refresh: skipping...' |
273 | 1 | tkerber | return
|
274 | 1 | tkerber | |
275 | 1 | tkerber | try:
|
276 | 1 | tkerber | f = open('/proc/%d/status' % os.getpid(), 'r') |
277 | 1 | tkerber | for line in f: |
278 | 1 | tkerber | k, v = line.decode('ascii').split(':') |
279 | 1 | tkerber | |
280 | 1 | tkerber | # Only refresh supported keys that are outdated (i.e. nan)
|
281 | 1 | tkerber | if k in self and np.isnan(self[k]): |
282 | 1 | tkerber | t, s = v.strip().split(None, 1) |
283 | 1 | tkerber | if self.verbose >= 2: |
284 | 1 | tkerber | print 'refresh: k=%s, t=%s, s=%s' % (k, t, s) |
285 | 1 | tkerber | self[k] = float(t) * self._scale[s.upper()] |
286 | 1 | tkerber | |
287 | 1 | tkerber | f.close() |
288 | 1 | tkerber | except (IOError, UnicodeError, ValueError): |
289 | 1 | tkerber | # Reset on error
|
290 | 1 | tkerber | self.clear()
|
291 | 1 | tkerber | |
292 | 1 | tkerber | def update(self, other=None): |
293 | 1 | tkerber | """Update VM statistics from a supplied dict, else clear and refresh.
|
294 | 1 | tkerber | D.update(E) -> None. Update D from E: for k in E.keys(): D[k] = E[k]"""
|
295 | 1 | tkerber | if self.verbose>=1: print 'MemoryStatistics.update' |
296 | 1 | tkerber | |
297 | 1 | tkerber | # Call to update without arguments has special meaning
|
298 | 1 | tkerber | if other is None: |
299 | 1 | tkerber | self.clear()
|
300 | 1 | tkerber | self.refresh()
|
301 | 1 | tkerber | else:
|
302 | 1 | tkerber | MemoryBase.update(self, other)
|
303 | 1 | tkerber | |
304 | 1 | tkerber | def __iadd__(self, other): |
305 | 1 | tkerber | """x.__iadd__(y) <==> x+=y"""
|
306 | 1 | tkerber | if self.verbose>=1: print 'MemoryStatistics.__iadd__(%s,%s)' \ |
307 | 1 | tkerber | % (object.__repr__(self), object.__repr__(other)) |
308 | 1 | tkerber | if isinstance(other, MemoryBase): |
309 | 1 | tkerber | self._values.__iadd__(other._values)
|
310 | 1 | tkerber | elif type(other) in [int,float]: |
311 | 1 | tkerber | self._values.__iadd__(other)
|
312 | 1 | tkerber | else:
|
313 | 1 | tkerber | raise TypeError('Unsupported operand type') |
314 | 1 | tkerber | return self |
315 | 1 | tkerber | |
316 | 1 | tkerber | def __isub__(self, other): |
317 | 1 | tkerber | """x.__isub__(y) <==> x-=y"""
|
318 | 1 | tkerber | if self.verbose>=1: print 'MemoryStatistics.__isub__(%s,%s)' \ |
319 | 1 | tkerber | % (object.__repr__(self), object.__repr__(other)) |
320 | 1 | tkerber | if isinstance(other, MemoryBase): |
321 | 1 | tkerber | self._values.__isub__(other._values)
|
322 | 1 | tkerber | elif type(other) in [int,float]: |
323 | 1 | tkerber | self._values.__isub__(other)
|
324 | 1 | tkerber | else:
|
325 | 1 | tkerber | raise TypeError('Unsupported operand type') |
326 | 1 | tkerber | return self |
327 | 1 | tkerber | |
328 | 1 | tkerber | # -------------------------------------------------------------------
|
329 | 1 | tkerber | |
330 | 1 | tkerber | #http://www.eecho.info/Echo/python/singleton/
|
331 | 1 | tkerber | #http://mail.python.org/pipermail/python-list/2007-July/622333.html
|
332 | 1 | tkerber | |
333 | 1 | tkerber | class Singleton(object): |
334 | 1 | tkerber | """A Pythonic Singleton object."""
|
335 | 1 | tkerber | def __new__(cls, *args, **kwargs): |
336 | 1 | tkerber | if '_inst' not in vars(cls): |
337 | 1 | tkerber | cls._inst = object.__new__(cls, *args, **kwargs)
|
338 | 1 | tkerber | #cls._inst = super(type, cls).__new__(cls, *args, **kwargs)
|
339 | 1 | tkerber | return cls._inst
|
340 | 1 | tkerber | |
341 | 1 | tkerber | class MemorySingleton(MemoryBase, Singleton): |
342 | 1 | tkerber | __doc__ = MemoryBase.__doc__ + """
|
343 | 1 | tkerber | The singleton variant is immutable once it has been instantiated, which
|
344 | 1 | tkerber | makes it suitable for recording the initial overhead of starting Python."""
|
345 | 1 | tkerber | |
346 | 1 | tkerber | def __init__(self, verbose=0): |
347 | 1 | tkerber | if verbose>=1: print 'MemorySingleton.__init__' |
348 | 1 | tkerber | if '_values' not in vars(self): |
349 | 1 | tkerber | if verbose>=1: print 'MemorySingleton.__init__ FIRST!' |
350 | 1 | tkerber | # Hack to circumvent singleton immutability
|
351 | 1 | tkerber | self.__class__ = MemoryStatistics
|
352 | 1 | tkerber | self.__init__(verbose)
|
353 | 1 | tkerber | self.__class__ = MemorySingleton
|
354 | 1 | tkerber | |
355 | 1 | tkerber | def __setitem__(self, key, value): |
356 | 1 | tkerber | """Disabled for the singleton.
|
357 | 1 | tkerber | x.__setitem__(i, y) <==> x[i]=y"""
|
358 | 1 | tkerber | if self.verbose>=2: print 'MemorySingleton.__setitem__' |
359 | 1 | tkerber | raise ReferenceError('Singleton is immutable.') |
360 | 1 | tkerber | |
361 | 1 | tkerber | def __delitem__(self, key): |
362 | 1 | tkerber | """Disabled for the singleton.
|
363 | 1 | tkerber | x.__delitem__(y) <==> del x[y]"""
|
364 | 1 | tkerber | if self.verbose>=2: print 'MemorySingleton.__delitem__' |
365 | 1 | tkerber | raise ReferenceError('Singleton is immutable.') |
366 | 1 | tkerber | |
367 | 1 | tkerber | def clear(self): |
368 | 1 | tkerber | """Disabled for the singleton.
|
369 | 1 | tkerber | D.clear() -> None. Remove all items from D."""
|
370 | 1 | tkerber | if self.verbose>=1: print 'MemorySingleton.clear' |
371 | 1 | tkerber | raise ReferenceError('Singleton is immutable.') |
372 | 1 | tkerber | |
373 | 1 | tkerber | def update(self): |
374 | 1 | tkerber | """Disabled for the singleton.
|
375 | 1 | tkerber | D.update(E) -> None. Update D from E: for k in E.keys(): D[k] = E[k]"""
|
376 | 1 | tkerber | if self.verbose>=1: print 'MemorySingleton.update' |
377 | 1 | tkerber | raise ReferenceError('Singleton is immutable.') |
378 | 1 | tkerber | |
379 | 1 | tkerber | def copy(self): |
380 | 1 | tkerber | """Return a shallow non-singleton copy of a VM statistics instance.
|
381 | 1 | tkerber | D.copy() -> a shallow copy of D"""
|
382 | 1 | tkerber | if self.verbose>=1: print 'MemorySingleton.copy' |
383 | 1 | tkerber | # Hack to circumvent singleton self-copy
|
384 | 1 | tkerber | self.__class__ = MemoryStatistics
|
385 | 1 | tkerber | res = self.copy()
|
386 | 1 | tkerber | self.__class__ = MemorySingleton
|
387 | 1 | tkerber | return res
|
388 | 1 | tkerber | |
389 | 1 | tkerber | # Make sure singleton is instantiated
|
390 | 1 | tkerber | MemorySingleton() |
391 | 1 | tkerber | |
392 | 1 | tkerber | # -------------------------------------------------------------------
|
393 | 1 | tkerber | |
394 | 1 | tkerber | # Helper functions for leak testing with NumPy arrays
|
395 | 1 | tkerber | |
396 | 1 | tkerber | def shapegen(size, ndims, ecc=0.5): |
397 | 1 | tkerber | """Return a generator of an N-dimensional array shape
|
398 | 1 | tkerber | which approximately contains a given number of elements.
|
399 | 1 | tkerber |
|
400 | 1 | tkerber | size: int or long in [1,inf[
|
401 | 1 | tkerber | The total number of elements
|
402 | 1 | tkerber | ndims=3: int in [1,inf[
|
403 | 1 | tkerber | The number of dimensions
|
404 | 1 | tkerber | ecc=0.5: float in ]0,1[
|
405 | 1 | tkerber | The eccentricity of the distribution
|
406 | 1 | tkerber | """
|
407 | 1 | tkerber | assert type(size) in [int,float] and size>=1 |
408 | 1 | tkerber | assert type(ndims) is int and ndims>=1 |
409 | 1 | tkerber | assert type(ecc) in [int,float] and ecc>0 and ecc<1 |
410 | 1 | tkerber | |
411 | 1 | tkerber | for i in range(ndims-1): |
412 | 1 | tkerber | scale = size**(1.0/(ndims-i))
|
413 | 1 | tkerber | c = round(np.random.uniform((1-ecc)*scale, 1.0/(1-ecc)*scale)) |
414 | 1 | tkerber | size/=c |
415 | 1 | tkerber | yield c
|
416 | 1 | tkerber | yield round(size) |
417 | 1 | tkerber | |
418 | 1 | tkerber | def shapeopt(maxseed, size, ndims, ecc=0.5): |
419 | 1 | tkerber | """Return optimal estimate of an N-dimensional array shape
|
420 | 1 | tkerber | which is closest to containing a given number of elements.
|
421 | 1 | tkerber |
|
422 | 1 | tkerber | maxseed: int in [1,inf[
|
423 | 1 | tkerber | The maximal number of seeds to try
|
424 | 1 | tkerber | size: int or long in [1,inf[
|
425 | 1 | tkerber | The total number of elements
|
426 | 1 | tkerber | ndims=3: int in [1,inf[
|
427 | 1 | tkerber | The number of dimensions
|
428 | 1 | tkerber | ecc=0.5: float in ]0,1[
|
429 | 1 | tkerber | The eccentricity of the distribution
|
430 | 1 | tkerber | """
|
431 | 1 | tkerber | assert type(maxseed) is int and maxseed>=1 |
432 | 1 | tkerber | assert type(size) in [int,float] and size>=1 |
433 | 1 | tkerber | assert type(ndims) is int and ndims>=1 |
434 | 1 | tkerber | assert type(ecc) in [int,float] and ecc>0 and ecc<1 |
435 | 1 | tkerber | |
436 | 1 | tkerber | digits_best = np.inf |
437 | 1 | tkerber | shape_best = None
|
438 | 1 | tkerber | for seed in range(maxseed): |
439 | 1 | tkerber | np.random.seed(seed) |
440 | 1 | tkerber | shape = tuple(shapegen(size, ndims, ecc))
|
441 | 1 | tkerber | if np.prod(shape) == size:
|
442 | 1 | tkerber | return -np.inf, shape
|
443 | 1 | tkerber | digits = np.log10(abs(np.prod(shape)-size))
|
444 | 1 | tkerber | if digits < digits_best:
|
445 | 1 | tkerber | (digits_best, shape_best) = (digits, shape) |
446 | 1 | tkerber | return digits_best, shape_best
|