Coverage for /builds/ericyuan00000/ase/ase/utils/timing.py: 85.71%
112 statements
« prev ^ index » next coverage.py v7.5.3, created at 2025-06-18 01:20 +0000
« prev ^ index » next coverage.py v7.5.3, created at 2025-06-18 01:20 +0000
1# fmt: off
3# Copyright (C) 2003 CAMP
4# Please see the accompanying LICENSE file for further information.
7import functools
8import inspect
9import sys
10import time
13def function_timer(func, *args, **kwargs):
14 out = kwargs.pop('timeout', sys.stdout)
15 t1 = time.time()
16 r = func(*args, **kwargs)
17 t2 = time.time()
18 print(t2 - t1, file=out)
19 return r
22class Timer:
23 """Timer object.
25 Use like this::
27 timer = Timer()
28 timer.start('description')
29 # do something
30 timer.stop()
32 or::
34 with timer('description'):
35 # do something
37 To get a summary call::
39 timer.write()
41 """
43 def __init__(self, print_levels=1000):
44 self.timers = {}
45 self.t0 = time.time()
46 self.running = []
47 self.print_levels = print_levels
49 def print_info(self, calc):
50 """Override to get to write info during calculator's initialize()."""
52 def start(self, name):
53 names = tuple(self.running + [name])
54 self.timers[names] = self.timers.get(names, 0.0) - time.time()
55 self.running.append(name)
57 def stop(self, name=None):
58 if name is None:
59 name = self.running[-1]
60 names = tuple(self.running)
61 running = self.running.pop()
62 if name != running:
63 raise RuntimeError('Must stop timers by stack order. '
64 'Requested stopping of %s but topmost is %s'
65 % (name, running))
66 self.timers[names] += time.time()
67 return names
69 def __call__(self, name):
70 """Context manager for timing a block of code.
72 Example (t is a timer object)::
74 with t('Add two numbers'):
75 x = 2 + 2
77 # same as this:
78 t.start('Add two numbers')
79 x = 2 + 2
80 t.stop()
81 """
82 self.start(name)
83 return self
85 def __enter__(self):
86 pass
88 def __exit__(self, *args):
89 self.stop()
91 def get_time(self, *names):
92 return self.timers[names]
94 def write(self, out=sys.stdout):
95 were_running = list(self.running)
96 while self.running:
97 self.stop()
98 if len(self.timers) == 0:
99 return
101 t0 = time.time()
102 tot = t0 - self.t0
104 n = max(len(names[-1]) + len(names) for names in self.timers) + 1
105 line = '-' * (n + 26) + '\n'
106 out.write('%-*s incl. excl.\n' % (n, 'Timing:'))
107 out.write(line)
108 tother = tot
110 inclusive = self.timers.copy()
111 exclusive = self.timers.copy()
112 keys = sorted(exclusive.keys())
113 for names in keys:
114 t = exclusive[names]
115 if len(names) > 1:
116 if len(names) < self.print_levels + 1:
117 exclusive[names[:-1]] -= t
118 else:
119 tother -= t
120 exclusive[('Other',)] = tother
121 inclusive[('Other',)] = tother
122 keys.append(('Other',))
123 for names in keys:
124 t = exclusive[names]
125 tinclusive = inclusive[names]
126 r = t / tot
127 p = 100 * r
128 i = int(40 * r + 0.5)
129 if i == 0:
130 bar = '|'
131 else:
132 bar = '|%s|' % ('-' * (i - 1))
133 level = len(names)
134 if level > self.print_levels:
135 continue
136 name = (level - 1) * ' ' + names[-1] + ':'
137 out.write('%-*s%9.3f %9.3f %5.1f%% %s\n' %
138 (n, name, tinclusive, t, p, bar))
139 out.write(line)
140 out.write('%-*s%9.3f %5.1f%%\n\n' % (n + 10, 'Total:', tot, 100.0))
142 for name in were_running:
143 self.start(name)
145 def add(self, timer):
146 for name, t in timer.timers.items():
147 self.timers[name] = self.timers.get(name, 0.0) + t
150class timer:
151 """Decorator for timing a method call.
153 Example::
155 from ase.utils.timing import timer, Timer
157 class A:
158 def __init__(self):
159 self.timer = Timer()
161 @timer('Add two numbers')
162 def add(self, x, y):
163 return x + y
165 """
167 def __init__(self, name):
168 self.name = name
170 def __call__(self, method):
171 if inspect.isgeneratorfunction(method):
172 @functools.wraps(method)
173 def new_method(slf, *args, **kwargs):
174 gen = method(slf, *args, **kwargs)
175 while True:
176 slf.timer.start(self.name)
177 try:
178 x = next(gen)
179 except StopIteration:
180 break
181 finally:
182 slf.timer.stop()
183 yield x
184 else:
185 @functools.wraps(method)
186 def new_method(slf, *args, **kwargs):
187 slf.timer.start(self.name)
188 x = method(slf, *args, **kwargs)
189 try:
190 slf.timer.stop()
191 except IndexError:
192 pass
193 return x
194 return new_method