[pypy-issue] Issue #1820: IPython timeit magic not working anymore. (pypy/pypy)
Yichao Yu
issues-reply at bitbucket.org
Sun Jul 20 09:06:40 CEST 2014
New issue 1820: IPython timeit magic not working anymore.
https://bitbucket.org/pypy/pypy/issue/1820/ipython-timeit-magic-not-working-anymore
Yichao Yu:
The following is a copy of the `%timeit` magic in IPython.
It works correctly (with limited function of course) on CPython 2 and 3 but the input statement does not work at all on pypy.
For testing, run `run_timeit("print(1)")`. On CPython, a lot of "1"'s are printed. On pypy, there's no output at all.
PyPy version, latest default 24db6697b691
ArchLinux x86_64, gcc 4.9, `CFLAGS=-O3`, pypy translated with `--shared`,
```python
import timeit, ast, sys
class TimeitTemplateFiller(ast.NodeTransformer):
"""Fill in the AST template. for timing execution.
This is quite closely tied to the template definition, which is in
:meth:`ExecutionMagics.timeit`.
"""
def __init__(self, ast_setup, ast_stmt):
self.ast_setup = ast_setup
self.ast_stmt = ast_stmt
def visit_FunctionDef(self, node):
"Fill in the setup statement"
self.generic_visit(node)
if node.name == "inner":
node.body[:1] = self.ast_setup.body
return node
def visit_For(self, node):
"Fill in the statement to be timed"
if getattr(getattr(node.body[0], 'value', None), 'id', None) == 'stmt':
node.body = self.ast_stmt.body
return node
def _format_time(timespan, precision=3):
"""Formats the timespan in a human readable form"""
import math
if timespan >= 60.0:
# we have more than a minute, format that in a human readable form
# Idea from http://snipplr.com/view/5713/
parts = [("d", 60*60*24),("h", 60*60),("min", 60), ("s", 1)]
time = []
leftover = timespan
for suffix, length in parts:
value = int(leftover / length)
if value > 0:
leftover = leftover % length
time.append(u'%s%s' % (str(value), suffix))
if leftover < 1:
break
return " ".join(time)
# Unfortunately the unicode 'micro' symbol can cause problems in
# certain terminals.
# See bug: https://bugs.launchpad.net/ipython/+bug/348466
# Try to prevent crashes by being more secure than it needs to
# E.g. eclipse is able to print a, but has no sys.stdout.encoding set.
units = [u"s", u"ms",u'us',"ns"] # the save value
if hasattr(sys.stdout, 'encoding') and sys.stdout.encoding:
try:
u'\xb5'.encode(sys.stdout.encoding)
units = [u"s", u"ms",u'\xb5s',"ns"]
except:
pass
scaling = [1, 1e3, 1e6, 1e9]
if timespan > 0.0:
order = min(-int(math.floor(math.log10(timespan)) // 3), 3)
else:
order = 3
return u"%.*g %s" % (precision, timespan * scaling[order], units[order])
def run_timeit(stmt):
timefunc = timeit.default_timer
number = 0
repeat = timeit.default_repeat
precision = 3
timer = timeit.Timer(timer=timefunc)
# this code has tight coupling to the inner workings of timeit.Timer,
# but is there a better way to achieve that the code stmt has access
# to the shell namespace?
# called as line magic
ast_setup = ast.parse("pass")
ast_stmt = ast.parse(stmt)
# This codestring is taken from timeit.template - we fill it in as an
# AST, so that we can apply our AST transformations to the user code
# without affecting the timing code.
timeit_ast_template = ast.parse('def inner(_it, _timer):\n'
' setup\n'
' _t0 = _timer()\n'
' for _i in _it:\n'
' stmt\n'
' _t1 = _timer()\n'
' return _t1 - _t0\n')
timeit_ast = TimeitTemplateFiller(ast_setup, ast_stmt).visit(timeit_ast_template)
timeit_ast = ast.fix_missing_locations(timeit_ast)
code = compile(timeit_ast, "<magic-timeit>", "exec")
ns = {}
exec(code, {}, ns)
timer.inner = ns["inner"]
if number == 0:
# determine number so that 0.2 <= total time < 2.0
number = 1
for _ in range(1, 10):
if timer.timeit(number) >= 0.2:
break
number *= 10
all_runs = timer.repeat(repeat, number)
best = min(all_runs) / number
print(u"%d loops, best of %d: %s per loop" % (number, repeat,
_format_time(best, precision)))
```
More information about the pypy-issue
mailing list