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

1# fmt: off 

2 

3# Copyright (C) 2003 CAMP 

4# Please see the accompanying LICENSE file for further information. 

5 

6 

7import functools 

8import inspect 

9import sys 

10import time 

11 

12 

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 

20 

21 

22class Timer: 

23 """Timer object. 

24 

25 Use like this:: 

26 

27 timer = Timer() 

28 timer.start('description') 

29 # do something 

30 timer.stop() 

31 

32 or:: 

33 

34 with timer('description'): 

35 # do something 

36 

37 To get a summary call:: 

38 

39 timer.write() 

40 

41 """ 

42 

43 def __init__(self, print_levels=1000): 

44 self.timers = {} 

45 self.t0 = time.time() 

46 self.running = [] 

47 self.print_levels = print_levels 

48 

49 def print_info(self, calc): 

50 """Override to get to write info during calculator's initialize().""" 

51 

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) 

56 

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 

68 

69 def __call__(self, name): 

70 """Context manager for timing a block of code. 

71 

72 Example (t is a timer object):: 

73 

74 with t('Add two numbers'): 

75 x = 2 + 2 

76 

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 

84 

85 def __enter__(self): 

86 pass 

87 

88 def __exit__(self, *args): 

89 self.stop() 

90 

91 def get_time(self, *names): 

92 return self.timers[names] 

93 

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 

100 

101 t0 = time.time() 

102 tot = t0 - self.t0 

103 

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 

109 

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

141 

142 for name in were_running: 

143 self.start(name) 

144 

145 def add(self, timer): 

146 for name, t in timer.timers.items(): 

147 self.timers[name] = self.timers.get(name, 0.0) + t 

148 

149 

150class timer: 

151 """Decorator for timing a method call. 

152 

153 Example:: 

154 

155 from ase.utils.timing import timer, Timer 

156 

157 class A: 

158 def __init__(self): 

159 self.timer = Timer() 

160 

161 @timer('Add two numbers') 

162 def add(self, x, y): 

163 return x + y 

164 

165 """ 

166 

167 def __init__(self, name): 

168 self.name = name 

169 

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