1 """benchmarking through py.test"""
4 from py
.__.test
.item
import Item
5 from py
.__.test
.terminal
.terminal
import TerminalSession
7 from math
import ceil
, floor
, log10
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
))
24 # like py.test Directory but scan for 'bench_<smth>.py'
25 class Directory(py
.test
.collect
.Directory
):
27 def filefilter(self
, path
):
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%
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")
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
)
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
76 self
.benchtitle
= src
.splitlines()[0].strip()
80 timer
= Timer(src
, globals=target
.func_globals
)
82 if self
.name
.startswith('timeit_'):
83 # from IPython.Magic.magic_timeit
87 t
= timer
.timeit(number
)
91 number
= int(ceil(number
))
95 # we are not close enough to that 0.2s
99 # since we are very close to be > 0.2s we'd better adjust number
100 # so that timing time is not too high
102 number
= int(ceil(number
))
106 self
.benchtime
= min(timer
.repeat(repeat
, number
)) / number
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")
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')
133 # benchname, time, benchtitle
136 for item
, outcome
in self
._memo
:
137 if isinstance(item
, Item
):
139 best
= item
.benchtime
142 # skipped or failed benchmarks
146 # from IPython.Magic.magic_timeit
148 order
= min(-int(floor(log10(best
)) // 3), 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...
173 wm
[un
] = max(len(m
), wm
[un
])
174 we
[un
] = max(len(e
), we
[un
])
196 # let's put the number into the right place
198 for i
in range(len(units
)):
202 txt
+= ' '*(wm
[i
]+we
[i
]+1)
204 s
[1] = '%s %s' % (txt
, u
)
207 # align all columns besides the last one
209 w
= max(len(s
[i
]) for s
in results
)
216 self
.out
.write('%s | %s | %s\n' % tuple(s
))
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
)