Preface
Timing and profiling code is all sorts of useful, and it’s also just good ol’ fashioned fun (and sometimes surprising!). In this post, I’ll introduce how to do the following through IPython magic functions:
Installation & Setup
Please follow instructions to install ipython:
Most of the functionality we’ll work with is included in the standard library, but if you’re interested in line-by-line or memory profiling, go ahead and run through this setup. First, install the following:
Next, create an IPython profile and extensions directory where we’ll configure a couple of missing magic functions:
Create the following IPython extention files with the contents below to define the magic functions:
Finally, register the extension modules you just created with the default IPython profile we made earlier:
And that’s it! We’re ready to time and profile to our hearts content. Start ipython and test for the following (executing command ipython):
- In [1]: %time?
- In [2]: %timeit?
- In [3]: %prun?
- In [4]: %lprun?
- In [5]: %mprun?
- In [6]: %memit?
Time profiling does exactly what it sounds like - it tells you how much time it took to execute a script, which may be a simple one-liner or a whole module.
%time
See how long it takes a script to run.
- In [7]: %time {1 for i in xrange(10*100000)}
- CPU times: user 76.6 ms, sys: 539 µs, total: 77.2 ms
- Wall time: 76.3 ms
- Out[7]: {1}
See how long a script takes to run averaged over multiple runs.
- In [8]: %timeit 10 * 100000
- 100000000 loops, best of 3: 10 ns per loop
- In [9]: %timeit -n 1000 10 * 100000
- 1000 loops, best of 3: 40.1 ns per loop
%prun
See how long it took each function in a script to run:
- In [10]: from time import sleep
- In [11]: def foo(): sleep(1)
- In [12]: def bar(): sleep(2)
- In [13]: def baz(): foo(), bar()
- In [14]: %prun baz()
- 7 function calls in 3.003 seconds
- Ordered by: internal time
- ncalls tottime percall cumtime percall filename:lineno(function)
- 2 3.003 1.502 3.003 1.502 {time.sleep}
- 1 0.000 0.000 3.003 3.003
13-aeee66c4e941>:1(baz) - 1 0.000 0.000 2.002 2.002
12-1428f9fb0b95>:1(bar) - 1 0.000 0.000 1.001 1.001
11-a559e91038d2>:1(foo) - 1 0.000 0.000 3.003 3.003
: 1() - 1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects}
See how long it took each line in a function to run.
Create and edit a new module named foo.py in the same directory where you started IPython. Paste the following code in the file and jump back to IPython.
- foo.py
- def foo(n):
- phrase = 'repeat me'
- pmul = phrase * n
- pjoi = ''.join([phrase for x in xrange(n)])
- pinc = ''
- for x in xrange(n):
- pinc += phrase
- del pmul, pjoi, pinc
- In [1]: from foo import foo
- In [2]: %lprun -f foo foo(10000)
- Timer unit: 1e-06 s
- Total time: 0.030291 s
- File: foo.py
- Function: foo at line 1
- Line # Hits Time Per Hit % Time Line Contents
- ==============================================================
- 1 def foo(n):
- 2 1 4.0 4.0 0.0 phrase = 'repeat me'
- 3 1 48.0 48.0 0.2 pmul = phrase * n
- 4 10001 10566.0 1.1 34.9 pjoi = ''.join([phrase for x in xrange(n)])
- 5 1 1.0 1.0 0.0 pinc = ''
- 6 10001 8804.0 0.9 29.1 for x in xrange(n):
- 7 10000 10866.0 1.1 35.9 pinc += phrase
- 8 1 2.0 2.0 0.0 del pmul, pjoi, pinc
%mprun
See how much memory a script uses line by line. Let’s take a look at the same foo() function that we profiled with %lprun - except this time we’re interested in incremental memory usage and not execution time:
- In [5]: %mprun -f foo foo(1000)
- Filename: foo.py
- Line # Mem usage Increment Line Contents
- ================================================
- 1 30.9 MiB 30.9 MiB def foo(n):
- 2 30.9 MiB 0.0 MiB phrase = 'repeat me'
- 3 30.9 MiB 0.0 MiB pmul = phrase * n
- 4 30.9 MiB 0.0 MiB pjoi = ''.join([phrase for x in xrange(n)])
- 5 30.9 MiB 0.0 MiB pinc = ''
- 6 30.9 MiB 0.0 MiB for x in xrange(n):
- 7 30.9 MiB 0.0 MiB pinc += phrase
- 8 30.9 MiB 0.0 MiB del pmul, pjoi, pinc
See how much memory a script uses overall. %memit works a lot like %timeit except that the number of iterations is set with -r instead of -n:
- In [6]: %memit -r 3 [x for x in xrange(10000)]
- peak memory: 31.00 MiB, increment: 0.05 MiB
Supplement
* Difference between CPU time and wall time
* Profiling Python Code
* Chapter 3 of the Python Data Analysis book
* Python MotW: Debugging and Profiling
沒有留言:
張貼留言