Skip to content

Instantly share code, notes, and snippets.

@andreberg
Last active February 24, 2020 18:01
Show Gist options
  • Save andreberg/5691839 to your computer and use it in GitHub Desktop.
Save andreberg/5691839 to your computer and use it in GitHub Desktop.
[Benchmark Decorator] Prints the time a function take to execute per call and cumulative total. Works with Python 2.7 and Python 3. #python #python3 #benchmark #decorator
# -*- coding: utf-8 -*-
#
# Created by André Berg on Jun 2, 2013.
# Copyright 2013 Berg Media. All rights reserved.
#
from functools import wraps, partial
def benchmark(func=None, prec=3, unit='auto', name_width=0, time_width=8):
"""
A decorator that prints the time a function takes
to execute per call and cumulative total.
Accepts the following keyword arguments:
`unit` str time unit for display. one of `[auto, us, ms, s, m]`.
`prec` int radix point precision.
`name_width` int width of the right-aligned function name field.
`time_width` int width of the right-aligned time value field.
For convenience you can also set attributes on the benchmark
function itself with the same name as the keyword arguments
and the value of those will be used instead. This saves you
from having to call the decorator with the same arguments each
time you use it. Just set, for example, `benchmark.prec = 5`
before you use the decorator for the first time.
"""
import time
if hasattr(benchmark, 'prec'):
prec = getattr(benchmark, 'prec')
if hasattr(benchmark, 'unit'):
unit = getattr(benchmark, 'unit')
if hasattr(benchmark, 'name_width'):
name_width = getattr(benchmark, 'name_width')
if hasattr(benchmark, 'time_width'):
time_width = getattr(benchmark, 'time_width')
if func is None:
return partial(benchmark, prec=prec, unit=unit,
name_width=name_width, time_width=time_width)
@wraps(func)
def wrapper(*args, **kwargs): # IGNORE:W0613
def _get_unit_mult(val, unit):
multipliers = {'us': 1000000.0, 'ms': 1000.0, 's': 1.0, 'm': (1.0 / 60.0)}
if unit in multipliers:
mult = multipliers[unit]
else: # auto
if val >= 60.0:
unit = "m"
elif val >= 1.0:
unit = "s"
elif val <= 0.001:
unit = "us"
else:
unit = "ms"
mult = multipliers[unit]
return (unit, mult)
t = time.clock()
res = func(*args, **kwargs)
td = (time.clock() - t)
wrapper.total += td
wrapper.count += 1
tt = wrapper.total
cn = wrapper.count
tdu, tdm = _get_unit_mult(td, unit)
ttu, ttm = _get_unit_mult(tt, unit)
td *= tdm
tt *= ttm
print(" -> {0:>{8}}() @ {1:>03}: {3:>{7}.{2}f} {4:>2}, total: {5:>{7}.{2}f} {6:>2}"
.format(func.__name__, cn, prec, td, tdu, tt, ttu, time_width, name_width))
return res
wrapper.total = 0
wrapper.count = 0
return wrapper
if __name__ == '__main__':
@benchmark
def bla(n, m):
return n ** m
for i in range(15):
bla(i, i ** 6)
# sample output:
# -> bla() @ 001: 6.000 us, total: 6.000 us
# -> bla() @ 002: 3.000 us, total: 9.000 us
# -> bla() @ 003: 12.000 us, total: 21.000 us
# -> bla() @ 004: 7.000 us, total: 28.000 us
# -> bla() @ 005: 33.000 us, total: 61.000 us
# -> bla() @ 006: 330.000 us, total: 391.000 us
# -> bla() @ 007: 1.921 ms, total: 2.312 ms
# -> bla() @ 008: 10.941 ms, total: 13.253 ms
# -> bla() @ 009: 4.479 ms, total: 17.732 ms
# -> bla() @ 010: 148.302 ms, total: 166.034 ms
# -> bla() @ 011: 377.794 ms, total: 543.828 ms
# -> bla() @ 012: 1.133 s, total: 1.677 s
# -> bla() @ 013: 1.754 s, total: 3.431 s
# -> bla() @ 014: 6.063 s, total: 9.494 s
# -> bla() @ 015: 11.817 s, total: 21.311 s
@tony
Copy link

tony commented Dec 20, 2013

@andreberg: can you MIT license this?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment