Thu 30 Jan 2014
Sometimes when you are coding, you want to know how long it takes for a particular function to run. This topic is known as profiling or performance tuning. Python has a couple of profilers built into its Standard Library, but for small pieces of code, it’s easier to just use Python’s timeit module. Thus, timeit will be the focus of this tutorial. The timeit module uses a platform-specific method to get the most accurate run time. Basically, the timeit module will do a setup once, run the code n number of times and returns the time it took to run. Usually it will output a “best of 3″ score. Oddly enough, the default number of times it runs the code is for 1,000,000 loops. The timeit does timing with time.time() on Linux / Mac and time.clock() on Windows to get the most accurate readings, which is something most people don’t think about.
You can run the timeit module from the command line or by importing it. We will look at both use cases.
timeit in the console
Using the timeit module on the command line is quite easy. Here are a couple of examples:
python -m timeit -s "[ord(x) for x in 'abcdfghi']
100000000 loops, best of 3: 0.0115 usec per loop
python -m timeit -s "[chr(int(x)) for x in '123456789']"
100000000 loops, best of 3: 0.0119 usec per loop
What’s going on here? Well, when you call Python on the command line and pass it the “-m” option, you are telling it to look up a module and use it as the main program. The “-s” tells the timeit module to run setup once. Then it runs the code for n number of loops 3 times and returns the best average of the 3 runs. For these silly examples, you won’t see much difference. Let’s take a quick look at timeit’s help so we can learn more about how it works:
C:\Users\mdriscoll>python -m timeit -h
Tool for measuring execution time of small code snippets.
This module avoids a number of common traps for measuring execution
times. See also Tim Peters' introduction to the Algorithms chapter in
the Python Cookbook, published by O'Reilly.
Library usage: see the Timer class.
Command line usage:
python timeit.py [-n N] [-r N] [-s S] [-t] [-c] [-h] [--] [statement]
-n/--number N: how many times to execute 'statement' (default: see below)
-r/--repeat N: how many times to repeat the timer (default 3)
-s/--setup S: statement to be executed once initially (default 'pass')
-t/--time: use time.time() (default on Unix)
-c/--clock: use time.clock() (default on Windows)
-v/--verbose: print raw timing results; repeat for more digits precision
-h/--help: print this usage message and exit
--: separate options from statement, use when statement starts with -
statement: statement to be timed (default 'pass')
A multi-line statement may be given by specifying each line as a
separate argument; indented lines are possible by enclosing an
argument in quotes and using leading spaces. Multiple -s options are
If -n is not given, a suitable number of loops is calculated by trying
successive powers of 10 until the total time is at least 0.2 seconds.
The difference in default timer function is because on Windows,
clock() has microsecond granularity but time()'s granularity is 1/60th
of a second; on Unix, clock() has 1/100th of a second granularity and
time() is much more precise. On either platform, the default timer
functions measure wall clock time, not the CPU time. This means that
other processes running on the same computer may interfere with the
timing. The best thing to do when accurate timing is necessary is to
repeat the timing a few times and use the best time. The -r option is
good for this; the default of 3 repetitions is probably enough in most
cases. On Unix, you can use clock() to measure CPU time.
Note: there is a certain baseline overhead associated with executing a
pass statement. The code here doesn't try to hide it, but you should
be aware of it. The baseline overhead can be measured by invoking the
program without arguments.
The baseline overhead differs between Python versions! Also, to
fairly compare older Python versions to Python 2.3, you may want to
use python -O for the older versions to avoid timing SET_LINENO
This tells use all the wonderful flags we can pass the timeit as well as what they do. It also tells us a little about how timeit works under the covers. Let’s write a silly function and see if we can time it from the command line:
# simple_func.py def my_function(): try: 1 / 0 except ZeroDivisionError: pass
All this function does is cause an error that is ignored. Yes, it’s a dumb example. To get timeit to run this code on the command line, we will need to import the code into its namespace, so make sure you have changed your current working directory to be in the same folder that this script is in. Then run the following:
python -m timeit "import simple_func; simple_func.my_function()"
1000000 loops, best of 3: 1.77 usec per loop
Here import the function and then call it. Note that we separate the import and the function call with semi-colons and that the Python code is in quotes. Now we’re ready to learn how to use timeit inside an actual Python script.
Importing timeit for Testing
Using the timeit module inside your code is also pretty easy. We’ll use the same silly script from before and show you how below:
def my_function(): try: 1 / 0 except ZeroDivisionError: pass if __name__ == "__main__": import timeit setup = "from __main__ import my_function" print timeit.timeit("my_function()", setup=setup)
Here we check to see if the script is being run directly (i.e. not imported). If it is, then we import timeit, create a setup string to import the function into timeit’s namespace and then we call timeit.timeit. You will note that we pass a call to the function in quotes, then the setup string. And that’s really all there is to it!
Now you know how to use the timeit module. It is really good at timing simple pieces of code. You would normally use it for code that you suspect is taking an inordinate amount of time to run. If you wanted more granular details about what’s going on in your code, then you would want to switch to a profiler. Have fun and happy coding!