Added a warning when constructing a Matrix without bracket + test modified
[sympy.git] / sympy / utilities / benchmarking.py
blobeb906f7b811dba6eaa777042c57465e5df1502e7
1 """benchmarking through py.test"""
3 import py
4 from py.__.test.item import Item
5 from py.__.test.terminal.terminal import TerminalSession
7 from math import ceil, floor, log10
8 from time import time
9 import timeit
11 from inspect import getsource
14 # from IPython.Magic.magic_timeit
15 #units = ["s", "ms", "\xc2\xb5s", "ns"]
16 units = ["s", "ms", "us", "ns"]
17 scaling = [1, 1e3, 1e6, 1e9]
19 unitn = dict((s,i) for i,s in enumerate(units))
21 precision = 3
24 # like py.test Directory but scan for 'bench_<smth>.py'
25 class Directory(py.test.collect.Directory):
27 def filefilter(self, path):
28 b = path.purebasename
29 ext = path.ext
30 return b.startswith('bench_') and ext == '.py'
33 # like py.test Module but scane for 'bench_<smth>' and 'timeit_<smth>'
34 class Module(py.test.collect.Module):
36 def funcnamefilter(self, name):
37 return name.startswith('bench_') or name.startswith('timeit_')
40 # Function level benchmarking driver
41 class Timer(timeit.Timer):
43 def __init__(self, stmt, setup='pass', timer=timeit.default_timer, globals=globals()):
44 # copy of timeit.Timer.__init__
45 # similarity index 95%
46 self.timer = timer
47 stmt = timeit.reindent(stmt, 8)
48 setup = timeit.reindent(setup, 4)
49 src = timeit.template % {'stmt': stmt, 'setup': setup}
50 self.src = src # Save for traceback display
51 code = compile(src, timeit.dummy_src_name, "exec")
52 ns = {}
53 #exec code in globals(), ns -- original timeit code
54 exec code in globals, ns # -- we use caller-provided globals instead
55 self.inner = ns["inner"]
59 class Function(py.__.test.item.Function):
61 def __init__(self, *args, **kw):
62 super(Function, self).__init__(*args, **kw)
63 self.benchtime = None
64 self.benchtitle = None
67 def execute(self, target, *args):
68 # get func source without first 'def func(...):' line
69 src = getsource(target)
70 src = '\n'.join( src.splitlines()[1:] )
72 # extract benchmark title
73 if target.func_doc is not None:
74 self.benchtitle = target.func_doc
75 else:
76 self.benchtitle = src.splitlines()[0].strip()
79 # XXX we ignore args
80 timer = Timer(src, globals=target.func_globals)
82 if self.name.startswith('timeit_'):
83 # from IPython.Magic.magic_timeit
84 repeat = 3
85 number = 1
86 for i in range(1,10):
87 t = timer.timeit(number)
89 if t >= 0.2:
90 number *= (0.2 / t)
91 number = int(ceil(number))
92 break
94 if t <= 0.02:
95 # we are not close enough to that 0.2s
96 number *= 10
98 else:
99 # since we are very close to be > 0.2s we'd better adjust number
100 # so that timing time is not too high
101 number *= (0.2 / t)
102 number = int(ceil(number))
103 break
106 self.benchtime = min(timer.repeat(repeat, number)) / number
108 # 'bench_<smth>'
109 else:
110 self.benchtime = timer.timeit(1)
113 class BenchSession(TerminalSession):
115 def header(self, colitems):
116 #self.out.sep("-", "benchmarking starts")
117 super(BenchSession, self).header(colitems)
119 def footer(self, colitems):
120 super(BenchSession, self).footer(colitems)
121 #self.out.sep("-", "benchmarking ends")
123 self.out.write('\n')
124 self.print_bench_results()
127 def print_bench_results(self):
128 self.out.write('==============================\n')
129 self.out.write(' *** BENCHMARKING RESULTS *** \n')
130 self.out.write('==============================\n')
131 self.out.write('\n')
133 # benchname, time, benchtitle
134 results = []
136 for item, outcome in self._memo:
137 if isinstance(item, Item):
139 best = item.benchtime
141 if best is None:
142 # skipped or failed benchmarks
143 tstr = '---'
145 else:
146 # from IPython.Magic.magic_timeit
147 if best > 0.0:
148 order = min(-int(floor(log10(best)) // 3), 3)
149 else:
150 order = 3
152 tstr = "%.*g %s" % (precision, best * scaling[order], units[order])
154 results.append( [item.name, tstr, item.benchtitle] )
156 # dot/unit align second column
157 # FIXME simpler? this is crappy -- shame on me...
158 wm = [0]*len(units)
159 we = [0]*len(units)
161 for s in results:
162 tstr = s[1]
163 n,u = tstr.split()
165 # unit n
166 un = unitn[u]
168 try:
169 m,e = n.split('.')
170 except ValueError:
171 m,e = n,''
173 wm[un] = max(len(m), wm[un])
174 we[un] = max(len(e), we[un])
176 for s in results:
177 tstr = s[1]
178 n,u = tstr.split()
180 un = unitn[u]
182 try:
183 m,e = n.split('.')
184 except ValueError:
185 m,e = n,''
187 m = m.rjust(wm[un])
188 e = e.ljust(we[un])
190 if e.strip():
191 n = '.'.join((m,e))
192 else:
193 n = ' '.join((m,e))
196 # let's put the number into the right place
197 txt = ''
198 for i in range(len(units)):
199 if i == un:
200 txt += n
201 else:
202 txt += ' '*(wm[i]+we[i]+1)
204 s[1] = '%s %s' % (txt, u)
207 # align all columns besides the last one
208 for i in range(2):
209 w = max(len(s[i]) for s in results)
211 for s in results:
212 s[i] = s[i].ljust(w)
214 # show results
215 for s in results:
216 self.out.write('%s | %s | %s\n' % tuple(s))
219 def main(args=None):
220 # hook our Directory/Module/Function as defaults
221 from py.__.test import defaultconftest
223 defaultconftest.Directory = Directory
224 defaultconftest.Module = Module
225 defaultconftest.Function = Function
227 # hook BenchSession as py.test session
228 config = py.test.config
229 config._getsessionclass = lambda : BenchSession
231 py.test.cmdline.main(args)