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