Skip to content

Commit 2f3d177

Browse files
committed
-
1 parent e23b208 commit 2f3d177

File tree

1 file changed

+97
-2
lines changed

1 file changed

+97
-2
lines changed

docs/topics/cute-profile.txt

Lines changed: 97 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,100 @@
66

77
.. _topics-cute-profile:
88

9-
:mod:`cute_profile` - documentation not written
10-
======================================
9+
:mod:`cute_profile`
10+
===================
11+
12+
The :mod:`cute_profile` module allows you to profile your code (i.e. find out
13+
which parts make it slow) by giving a nicer interface to the :mod:`cProfile`
14+
library from Python's standard library.
15+
16+
17+
What is "profiling"?
18+
--------------------
19+
20+
(Programmers experienced with profilers may skip this section.)
21+
22+
To "profile" a piece of code means to run it while checking how long it takes,
23+
and how long each function call inside the code takes. When you use a
24+
"profiler" to profile your program, you get a table of (a) all the functions
25+
calls that were made by the program, (b) how many times each function was
26+
called and (c) how long the function calls took.
27+
28+
A profiler is an indispensable programming tool, because it allows the
29+
programmer to understand which parts of his code take the longest. Usually,
30+
when using a profiler, you discover that only a few small parts of his code
31+
take most of the runtime of your program. And quite often, it's not the parts
32+
of code that you *thought* were the slow ones.
33+
34+
Once you realize which parts of the program cause slowness, you can focus your
35+
efforts on those problematic parts only, optimizing them or possibly
36+
redesigning the way they work so they're not slow anymore. Then the whole
37+
program becomes faster.
38+
39+
Profiling Python code with :mod:`cute_profile`
40+
----------------------------------------------
41+
42+
Python supplies a module called :mod:`cProfile` in its standard library.
43+
:mod:`cProfile` is a good profiler, but its interface can be inconvenient to
44+
work with. The :mod:`cute_profile` module has a more flexible interface, and it
45+
uses :mod:`cProfile` under the hood to do the actual profiling.
46+
47+
Let's profile an example program. Our example would be a function called
48+
``get_perfects``, which finds `perfect numbers`_:
49+
50+
>>> def get_divisors(x):
51+
... '''Get all the integer divisors of `x`.'''
52+
... return [i for i in xrange(1, x) if (x % i == 0)]
53+
...
54+
>>> def is_perfect(x):
55+
... '''Is the number `x` perfect?'''
56+
... return sum(get_divisors(x)) == x
57+
...
58+
>>> def get_perfects(top):
59+
... '''Get all the perfect numbers up to the number `top`.'''
60+
... return [i for i in xrange(1, top) if is_perfect(i)]
61+
>>> print(get_perfects(20000))
62+
63+
The result is ``[6, 28, 496, 8128]``. However, this function takes a few
64+
seconds to run. That's fairly long. Let's use :mod:`cute_profile` to find out
65+
*why* this function is taking so long. We'll add the
66+
:func:`cute_profile.profile_ready` decorator around `get_perfects`:
67+
68+
>>> from python_toolbox import cute_profile
69+
>>> @cute_profile.profile_ready()
70+
... def get_perfects(top):
71+
... '''Get all the perfect numbers up to the number `top`.'''
72+
... return [i for i in xrange(1, top) if is_perfect(i)]
73+
74+
75+
Now before we run ``get_perfects``, we set it to profile:
76+
77+
>>> get_perfects.profiling_on = True
78+
79+
And now we run it:
80+
81+
>>> print(get_perfects(20000))
82+
83+
We still get the same result, but now a profiling table gets printed:
84+
85+
6000 function calls in 6.142 seconds
86+
87+
Ordered by: cumulative time
88+
89+
ncalls tottime percall cumtime percall filename:lineno(function)
90+
1 0.000 0.000 6.142 6.142 :1()
91+
1 0.025 0.025 6.142 6.142 perfect_numbers.py:9(get_perfects)
92+
1999 0.045 0.000 6.117 0.003 perfect_numbers.py:6(is_perfect)
93+
1999 6.060 0.003 6.060 0.003 perfect_numbers.py:3(get_divisors)
94+
1999 0.012 0.000 0.012 0.000 {sum}
95+
1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects}
96+
</pre>
97+
This table shows how long each function took. If you want to understand *exactly* what each number says in this table, <a href="http://docs.python.org/library/profile.html#cProfile.run">there's an explanation here</a>.
98+
The <code>tottime</code> column says how much time was spent inside this function, across all calls, and without counting the time that was spent in sub-functions. See how the <code>get_divisors</code> function in our example has a very high <code>tottime</code> of 6.060. This means that <code>get_divisors</code> is what's causing our program to run slow, and if we'll want to optimize the program, we should try to come up with a smarter way of finding all of a number's divisors than going one-by-one over all numbers.
99+
<code>profile_ready</code> has a bunch of other options that I won't explore there. In brief:
100+
<ul><li>The <code>condition</code> argument is something like a "breakpoint condition" in an IDE: It can be a function, usually a lambda, that takes the decorated function and any arguments and returns whether or not to profile it this time.</li>
101+
<li><code>off_after</code> means whether you want the function to stop being profiled after being profiled one time. Default is <code>True</code>.</li>
102+
<li><code>sort</code> is an integer saying by which column the final results table should be sorted.</li>
103+
104+
105+
.. _perfect numbers: http://en.wikipedia.org/wiki/Perfect_number

0 commit comments

Comments
 (0)