Skip to content

Instantly share code, notes, and snippets.

@pmav99
Created June 26, 2014 19:58
Show Gist options
  • Save pmav99/fec0753c500f63790a7e to your computer and use it in GitHub Desktop.
Save pmav99/fec0753c500f63790a7e to your computer and use it in GitHub Desktop.

Some functions for helping timing stuff.

Note: The best timer is platform dependent. So always use from timeit import default_timer.

Other approaches

Custom Timers

#! /usr/bin/env python
# -*- coding: utf-8 -*-

"""
Just some classes that help benchmark execution speed.

Timer    : A context manager
AutoTimer: A self-adjusting timer. It replicates the behavior of the timeit module.

"""

from __future__ import division
from __future__ import print_function

import gc
import timeit

__all__ = ["Timer", "AutoTimer"]


def format_timing(timing, number_of_loops=1):
    """
    Return timing + unit

    Input
    =====

    timing         : The timing in seconds.
    number_of_loops: The number of loops that were required to get this timing value.
                     It defaults to 1.

    """
    usec = timing * 1e6 / number_of_loops
    if usec < 1000:
        return usec, "usec"
    else:
        msec = usec / 1000
        if msec < 1000:
            return msec, "msec"
        else:
            return timing, "sec"


class Timer(object):
    """
    A context manager that can be used to benchmark blocks of code (function calls etc).

    Usage
    =====

    It should be used to code blocks of code that take "significant" time to be executed
    (e.g. milli-seconds). It is not suitable for micro-benchmarks because it introduces
    some overhead.

    Example
    =======

        >>> with Timer() as t:
        >>>     # whatever ...

    See also
    ========

    Something like this may end up in the Standard library: http://bugs.python.org/issue19495

    """
    def __init__(self, timer=None, disable_gc=True, verbose=True, precision=3):
        if timer is None:
            timer = timeit.default_timer
        self.timer = timer
        self.disable_gc = disable_gc
        self.verbose = verbose
        self.precision = precision
        self.start = self.end = self.interval = None

    def __enter__(self):
        if self.disable_gc:
            self.gc_state = gc.isenabled()
            gc.disable()
        self.start = self.timer()
        return self

    def __exit__(self, *args):
        self.end = self.timer()
        if self.disable_gc and self.gc_state:
            gc.enable()

        self.interval, self.unit = format_timing(self.end - self.start)
        if self.verbose:
            print('time taken: %.*g %s' % (self.precision, self.interval, self.unit))


class AutoTimer(timeit.Timer):
    """
    An auto-adjustable Timer. It subclasses timeit.Timer and adds a new method named `auto()`.

    This one is suitable for micro benchmarks. It can be used with both python functions and
    python statements (e.g. strings)

    Usage
    =====

    >>> def f():
    >>>     return [i**2 for i in range(int(1e2))]

    >>> best = AutoTimer(f).auto(verbose=True, repeat=10)
    >>> print(best)

    See also
    ========

    There is an open bug for this one too: http://bugs.python.org/issue6422

    The documentation: https://docs.python.org/3.4/library/timeit.html#timeit.Timer

    """
    def auto(self, repeat=3, verbose=True, precision=3):
        """ Auto-determine how many times to execute the given statement """
        # determine number of required loops so that 0.2 <= total time < 2.0
        number_of_loops = 0
        for i in range(1, 10):
            number_of_loops = 10**i
            try:
                execution_time = self.timeit(number_of_loops)
            except Exception:
                self.print_exc()
                return 1
            if verbose:
                print("%d loops -> %.*g secs" % (number_of_loops, precision, execution_time))
            if execution_time >= 0.2:
                break

        # now that we have determined how many times we should loop,
        # execute the statements as many times as we need
        try:
            r = self.repeat(repeat, number_of_loops)
        except Exception:
            self.print_exc()
            return 1
        best = min(r)
        timing, unit = format_timing(best, number_of_loops)

        # print results
        if verbose:
            print("raw times:", " ".join(["%.*g" % (precision, x) for x in r]))
        print("%d loops, best of %d: %.*g %s per loop" % (number_of_loops, repeat, precision, timing, unit))

        return best


if __name__ == "__main__":
    def f():
        return [i**2 for i in range(int(1e2))]

    best = AutoTimer(f).auto(verbose=True, repeat=10)

    print()
    print("context manager")

    with Timer() as t:
        f()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment