Did you know — Python Tips

Measure the execution time of your Python codes

Ujjwal Dalmia
Towards Data Science

--

Photo by NeONBRAND on Unsplash

As we start becoming proficient in a programming language, we aspire to not only deliver the end objective but also to make our programs efficient.

In this tutorial of Did you know series we will learn about a few Ipython’s magic commands which can help us time profile our Python codes.

Note that, for the purpose of this tutorial, it is recommended to use Anaconda distribution.

1.) Profiling a single line of code

To check the execution time of a single line of python code, use %timeit. A simple example below to see how it works:

#### Simple usage of magics command %timeit
%timeit [num for num in range(20)]
#### Output
1.08 µs ± 43 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

Key notes:

  • Use %timeit just before the line of code to be profiled
  • It returns the average and standard deviation of code run time calculated over r number or runs and n number of loops within each run. In the above example, the list comprehension was evaluated for 7 runs with each run having 1 million loops (default behaviour). This took an average of 1.08 microsecond with a standard deviation of 43 nanoseconds.
  • One can customise the number of runs and loops while calling the magic command. The example is below for reference:
#### Customizing number of runs and loops in %timeit magic command
%timeit -r5 -n100 [num for num in range(20)]
1.01 µs ± 5.75 ns per loop (mean ± std. dev. of 5 runs, 100 loops each)

Using command options -r and -n, indicating # of runs and count of loops respectively, we have customised the time profile operation to 5 runs and 100 loops within each run.

2.) Profiling multiple lines of code

This section takes a step forward and explains how one can profile a complete code block. By making a small modification to %timeit magic command, replacing single percentage (%) with double percentage (%%), one can profile a complete code block. A sample demonstration for reference below:

#### Time profiling the code block using %%timeit
%%timeit -r5 -n1000
for i in range(10):
n = i**2
m = i**3
o = abs(i)
#### Output
10.5 µs ± 226 ns per loop (mean ± std. dev. of 5 runs, 1000 loops each)

It can be observed that the average execution time of the for loop is 10.5 microseconds. Note that the same command options -r and -n are use to control the count of runs and # of loops within each run respectively.

3.) Time profiling each line of code within a code block

Till now, we have only looked at summary stats when profiling single line of code or a complete code block. What if we want to evaluate the performance of each line of code within the code block? Line profiler package to the rescue!!!

Line_profiler package can be used to perform line by line profiling of any function. To work with line_profiler package, take the following steps:

  • Install package — The line profiler package can be installed with a simple call to pip or conda install. If one is using anaconda distribution for Python, using conda install is recommended
#### Installing line_profiler package
conda install line_profiler
  • Load Extension — Once installed, you can use IPython to load the line_profiler IPython extension, offered as part of this package:
#### Loading line_profiler's Ipython extension
%load_ext line_profiler
  • Time Profile the Function — Once loaded, use the following syntax to time profile any predefined function
%lprun -f function_name_only function_call_with_arguments

Syntax details:

  • The call to line_profiler starts with keyword %lprun and is followed by command option -f
  • The command option is then followed by the function name and then the function call

For the purpose of this exercise, we will define a function accepting list of heights (in meter) and weight (in lbs) as inputs and converts these in to centimetre and kg respectively.

#### Defining the function
def conversion(ht_mtrs, wt_lbs ):
ht_cms = [ht*100 for ht in ht_mtrs]
wt_kgs = [wt*.4535 for wt in wt_lbs]
#### Defining the height and weight lists:
ht = [5,5,4,7,6]
wt = [108, 120, 110, 98]
#### Profiling the function using line_profiler
%lprun -f conversion conversion(ht,wt)
---------------------------------------------------------------
#### Output
Total time: 1.46e-05 s
File: <ipython-input-13-41e195af43a9>Function: conversion at line 2Line # Hits Time Per Hit % Time Line Contents
==============================================================
2 1 105.0 105.0 71.9 ht_cms = [ht*100 for ht in ht_mtrs]
3 1 41.0 41.0 28.1 wt_kgs = [wt*.4535 for wt in wt_lbs]

Output Details:

  • Complete time profile is done in units of 14.6 microseconds (refer to the first line of output)

The table generated, has 6 columns:

  • Column 1 (Line #) — Line number of code (note that line # 1 is deliberately omitted from output as it was just the function definition statement)
  • Column 2 (Hits) — Number of times the line was called
  • Column 3 (Time) — Number of time units (each time unit is 14.6 micro seconds) spent on the line of code
  • Column 4 (Per Hit)- Column 3 divided by Column 2
  • Column 5 (%Time) — Of the total time spent, what % of time was spent on that particular line of code
  • Column 6 (Content)- Content of the line of code

One can clearly notice that conversion of height from meter to centimetre has taken almost 72% of the total time spent.

Closing note

With the execution time of each line of code at our disposal, we can deploy strategies to make our code efficient. In our next 3 tutorials, we will share some best practices to help you make your codes efficient.

I hope this tutorial of Did you know series was informative and you learned something new.

Will try and bring more interesting topics in future tutorials. Till then:

HAPPY LEARNING! ! ! !

--

--