%time
: Time the execution of a single statement%timeit
: Time repeated execution of a single statement for more accuracy%prun
: Run code with the profiler%lprun
: Run code with the line-by-line profiler%memit
: Measure the memory use of a single statement%mprun
: Run code with the line-by-line memory profilerline_profiler
and memory_profiler
extensions, which we will discuss in the following sections.%timeit
and %time
%timeit
line-magic and %%timeit
cell-magic in the introduction to magic functions in IPython Magic Commands; it can be used to time the repeated execution of snippets of code:%timeit sum(range(100))
%timeit
automatically does a large number of repetitions.
For slower commands, %timeit
will automatically adjust and perform fewer repetitions:%%timeit
total = 0
for i in range(1000):
for j in range(1000):
total += i * (-1) ** j
import random
L = [random.random() for i in range(100000)]
%timeit L.sort()
%time
magic function may be a better choice. It also is a good choice for longer-running commands, when short, system-related delays are unlikely to affect the result.
Let's time the sorting of an unsorted and a presorted list:import random
L = [random.random() for i in range(100000)]
print("sorting an unsorted list:")
%time L.sort()
print("sorting an already sorted list:")
%time L.sort()
%time
versus %timeit
, even for the presorted list!
This is a result of the fact that %timeit
does some clever things under the hood to prevent system calls from interfering with the timing.
For example, it prevents cleanup of unused Python objects (known as garbage collection) which might otherwise affect the timing.
For this reason, %timeit
results are usually noticeably faster than %time
results.%time
as with %timeit
, using the double-percent-sign cell magic syntax allows timing of multiline scripts:%%time
total = 0
for i in range(1000):
for j in range(1000):
total += i * (-1) ** j
%time
and %timeit
, as well as their available options, use the IPython help functionality (i.e., type %time?
at the IPython prompt).%prun
%prun
.def sum_of_lists(N):
total = 0
for i in range(5):
L = [j ^ (j >> i) for j in range(N)]
total += sum(L)
return total
%prun
with a function call to see the profiled results:%prun sum_of_lists(1000000)
14 function calls in 0.714 seconds
Ordered by: internal time
ncalls tottime percall cumtime percall filename:lineno(function)
5 0.599 0.120 0.599 0.120 <ipython-input-19>:4(<listcomp>)
5 0.064 0.013 0.064 0.013 {built-in method sum}
1 0.036 0.036 0.699 0.699 <ipython-input-19>:1(sum_of_lists)
1 0.014 0.014 0.714 0.714 <string>:1(<module>)
1 0.000 0.000 0.714 0.714 {built-in method exec}
sum_of_lists
.
From here, we could start thinking about what changes we might make to improve the performance in the algorithm.%prun
, as well as its available options, use the IPython help functionality (i.e., type %prun?
at the IPython prompt).%lprun
%prun
is useful, but sometimes it's more convenient to have a line-by-line profile report.
This is not built into Python or IPython, but there is a line_profiler
package available for installation that can do this.
Start by using Python's packaging tool, pip
, to install the line_profiler
package:$ pip install line_profiler
line_profiler
IPython extension, offered as part of this package:%load_ext line_profiler
%lprun
command will do a line-by-line profiling of any function–in this case, we need to tell it explicitly which functions we're interested in profiling:%lprun -f sum_of_lists sum_of_lists(5000)
Timer unit: 1e-06 s
Total time: 0.009382 s
File: <ipython-input-19-fa2be176cc3e>
Function: sum_of_lists at line 1
Line # Hits Time Per Hit % Time Line Contents
==============================================================
1 def sum_of_lists(N):
2 1 2 2.0 0.0 total = 0
3 6 8 1.3 0.1 for i in range(5):
4 5 9001 1800.2 95.9 L = [j ^ (j >> i) for j in range(N)]
5 5 371 74.2 4.0 total += sum(L)
6 1 0 0.0 0.0 return total
%lprun
, as well as its available options, use the IPython help functionality (i.e., type %lprun?
at the IPython prompt).%memit
and %mprun
memory_profiler
.
As with the line_profiler
, we start by pip
-installing the extension:$ pip install memory_profiler
%load_ext memory_profiler
%memit
magic (which offers a memory-measuring equivalent of %timeit
) and the %mprun
function (which offers a memory-measuring equivalent of %lprun
).
The %memit
function can be used rather simply:%memit sum_of_lists(1000000)
%mprun
magic.
Unfortunately, this magic works only for functions defined in separate modules rather than the notebook itself, so we'll start by using the %%file
magic to create a simple module called mprun_demo.py
, which contains our sum_of_lists
function, with one addition that will make our memory profiling results more clear:%%file mprun_demo.py
def sum_of_lists(N):
total = 0
for i in range(5):
L = [j ^ (j >> i) for j in range(N)]
total += sum(L)
del L # remove reference to L
return total
from mprun_demo import sum_of_lists
%mprun -f sum_of_lists sum_of_lists(1000000)