Coverage for /builds/kinetik161/ase/ase/utils/timing.py: 85.71%

112 statements  

« prev     ^ index     » next       coverage.py v7.2.7, created at 2023-12-10 11:04 +0000

1# Copyright (C) 2003 CAMP 

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

3 

4 

5import functools 

6import inspect 

7import sys 

8import time 

9 

10 

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 

18 

19 

20class Timer: 

21 """Timer object. 

22 

23 Use like this:: 

24 

25 timer = Timer() 

26 timer.start('description') 

27 # do something 

28 timer.stop() 

29 

30 or:: 

31 

32 with timer('description'): 

33 # do something 

34 

35 To get a summary call:: 

36 

37 timer.write() 

38 

39 """ 

40 

41 def __init__(self, print_levels=1000): 

42 self.timers = {} 

43 self.t0 = time.time() 

44 self.running = [] 

45 self.print_levels = print_levels 

46 

47 def print_info(self, calc): 

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

49 

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) 

54 

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('Must stop timers by stack order. ' 

62 'Requested stopping of %s but topmost is %s' 

63 % (name, running)) 

64 self.timers[names] += time.time() 

65 return names 

66 

67 def __call__(self, name): 

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

69 

70 Example (t is a timer object):: 

71 

72 with t('Add two numbers'): 

73 x = 2 + 2 

74 

75 # same as this: 

76 t.start('Add two numbers') 

77 x = 2 + 2 

78 t.stop() 

79 """ 

80 self.start(name) 

81 return self 

82 

83 def __enter__(self): 

84 pass 

85 

86 def __exit__(self, *args): 

87 self.stop() 

88 

89 def get_time(self, *names): 

90 return self.timers[names] 

91 

92 def write(self, out=sys.stdout): 

93 were_running = list(self.running) 

94 while self.running: 

95 self.stop() 

96 if len(self.timers) == 0: 

97 return 

98 

99 t0 = time.time() 

100 tot = t0 - self.t0 

101 

102 n = max([len(names[-1]) + len(names) for names in self.timers]) + 1 

103 line = '-' * (n + 26) + '\n' 

104 out.write('%-*s incl. excl.\n' % (n, 'Timing:')) 

105 out.write(line) 

106 tother = tot 

107 

108 inclusive = self.timers.copy() 

109 exclusive = self.timers.copy() 

110 keys = sorted(exclusive.keys()) 

111 for names in keys: 

112 t = exclusive[names] 

113 if len(names) > 1: 

114 if len(names) < self.print_levels + 1: 

115 exclusive[names[:-1]] -= t 

116 else: 

117 tother -= t 

118 exclusive[('Other',)] = tother 

119 inclusive[('Other',)] = tother 

120 keys.append(('Other',)) 

121 for names in keys: 

122 t = exclusive[names] 

123 tinclusive = inclusive[names] 

124 r = t / tot 

125 p = 100 * r 

126 i = int(40 * r + 0.5) 

127 if i == 0: 

128 bar = '|' 

129 else: 

130 bar = '|%s|' % ('-' * (i - 1)) 

131 level = len(names) 

132 if level > self.print_levels: 

133 continue 

134 name = (level - 1) * ' ' + names[-1] + ':' 

135 out.write('%-*s%9.3f %9.3f %5.1f%% %s\n' % 

136 (n, name, tinclusive, t, p, bar)) 

137 out.write(line) 

138 out.write('%-*s%9.3f %5.1f%%\n\n' % (n + 10, 'Total:', tot, 100.0)) 

139 

140 for name in were_running: 

141 self.start(name) 

142 

143 def add(self, timer): 

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

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

146 

147 

148class timer: 

149 """Decorator for timing a method call. 

150 

151 Example:: 

152 

153 from ase.utils.timing import timer, Timer 

154 

155 class A: 

156 def __init__(self): 

157 self.timer = Timer() 

158 

159 @timer('Add two numbers') 

160 def add(self, x, y): 

161 return x + y 

162 

163 """ 

164 

165 def __init__(self, name): 

166 self.name = name 

167 

168 def __call__(self, method): 

169 if inspect.isgeneratorfunction(method): 

170 @functools.wraps(method) 

171 def new_method(slf, *args, **kwargs): 

172 gen = method(slf, *args, **kwargs) 

173 while True: 

174 slf.timer.start(self.name) 

175 try: 

176 x = next(gen) 

177 except StopIteration: 

178 break 

179 finally: 

180 slf.timer.stop() 

181 yield x 

182 else: 

183 @functools.wraps(method) 

184 def new_method(slf, *args, **kwargs): 

185 slf.timer.start(self.name) 

186 x = method(slf, *args, **kwargs) 

187 try: 

188 slf.timer.stop() 

189 except IndexError: 

190 pass 

191 return x 

192 return new_method