June 17, 2012

Python Timer Class - Context Manager for Timing Code Blocks

Here is a handy Python Timer class. It creates a context manager object, used for timing a block of code.

from timeit import default_timer


class Timer(object):
    def __init__(self, verbose=False):
        self.verbose = verbose
        self.timer = default_timer
        
    def __enter__(self):
        self.start = self.timer()
        return self
        
    def __exit__(self, *args):
        end = self.timer()
        self.elapsed_secs = end - self.start
        self.elapsed = self.elapsed_secs * 1000  # millisecs
        if self.verbose:
            print 'elapsed time: %f ms' % self.elapsed

To use the Timer (context manager object), invoke it using Python's `with` statement. The duration of the context (code inside your `with` block) will be timed. It uses the appropriate timer for your platform, via the `timeit` module.

Timer is used like this:

with Timer() as target:
    # block of code goes here.
    # result (elapsed time) is stored in `target` properties.

Example script:
timing a web request (HTTP GET), using the `requests` module.

#!/usr/bin/env python

import requests
from timer import Timer

url = 'https://github.com/timeline.json'

with Timer() as t:
    r = requests.get(url)
    
print 'fetched %r in %.2f millisecs' % (url, t.elapsed)

Output:

fetched 'https://github.com/timeline.json' in 458.76 millisecs

`timer.py` in GitHub Gist form, with more examples:

#!/usr/bin/env python
#
# Python Timer Class - Context Manager for Timing Code Blocks
# Corey Goldberg - 2012
#
from timeit import default_timer
class Timer(object):
def __init__(self, verbose=False):
self.verbose = verbose
self.timer = default_timer
def __enter__(self):
self.start = self.timer()
return self
def __exit__(self, *args):
end = self.timer()
self.elapsed_secs = end - self.start
self.elapsed = self.elapsed_secs * 1000 # millisecs
if self.verbose:
print 'elapsed time: %f ms' % self.elapsed
if __name__ == '__main__':
# example:
# 'HTTP GET' from requests module, inside timer blocks.
# invoke the Timer context manager using the `with` statement.
import requests
url = 'https://github.com/timeline.json'
# verbose (auto) timer output
with Timer(verbose=True):
r = requests.get(url)
# print stored elapsed time in milliseconds
with Timer() as t:
r = requests.get(url)
print 'response time (millisecs): %.2f' % t.elapsed
# print stored elapsed time in seconds
with Timer() as t:
r = requests.get(url)
print 'response time (secs): %.3f' % t.elapsed_secs
# example output:
#
# $ python timer.py
# elapsed time: 652.403831 ms
# response time (millisecs): 635.49
# response time (secs): 0.624
view raw timer.py hosted with ❤ by GitHub

8 comments:

Eli Bendersky said...

I have something very similar in my utils, but you can also give it a name: https://gist.github.com/2944722

Anonymous said...

I've been using decorators for this. I'll have to see how hard it would be to add context manager syntax support in profilehooks.

viajar-uruguay said...

Very cool tool! timeit can be a pain to use, because of it string statement argument.

This seems much more natural to use. Kudos!

Will McGugan said...

I did something similar. I also added an option to append the timings to a CSV, since I was trying to optimize a time sensitive piece of code.

I wasn't aware of default_timer. TIL. :-)

https://gist.github.com/2948552

mkaz said...

Thanks!

Very useful timer class, a good use of context manager

Anonymous said...

https://github.com/jsocol/pystatsd/blob/master/statsd/client.py - similar idea also usable as a decorator.

Chris K said...

I am receiving an error in attempting to import the requests module. Is this pseudo-code? From where are your importing this?

Corey Goldberg said...

@chrisk

you need to install `requests` (a *great* module).

see: http://docs.python-requests.org/

or `pip install requests`

-Corey