Project Euler and other coding contests often have a maximum time to run or people boast of how fast their particular solution runs. With Python, sometimes the approaches are somewhat kludgey - i.e., adding timing code to __main__
.
What is a good way to profile how long a Python program takes to run?
34 Answers 34
Python includes a profiler called cProfile
. It not only gives the total running time, but also times each function separately, and tells you how many times each function was called, making it easy to determine where you should make optimizations.
You can call it from within your code, or from the interpreter, like this:
import cProfile
cProfile.run('foo()')
Even more usefully, you can invoke cProfile when running a script:
python -m cProfile myscript.py
Or when running a module:
python -m cProfile -m mymodule
To make it even easier, I made a little batch file called 'profile.bat':
python -m cProfile %1
So all I have to do is run:
profile euler048.py
And I get this:
1007 function calls in 0.061 CPU seconds
Ordered by: standard name
ncalls tottime percall cumtime percall filename:lineno(function)
1 0.000 0.000 0.061 0.061 <string>:1(<module>)
1000 0.051 0.000 0.051 0.000 euler048.py:2(<lambda>)
1 0.005 0.005 0.061 0.061 euler048.py:2(<module>)
1 0.000 0.000 0.061 0.061 {execfile}
1 0.002 0.002 0.053 0.053 {map}
1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler objects}
1 0.000 0.000 0.000 0.000 {range}
1 0.003 0.003 0.003 0.003 {sum}
For more information, check out this tutorial from PyCon 2013 titled
Python Profiling
Also via YouTube.
-
306Also it is useful to sort the results, that can be done by -s switch, example: '-s time'. You can use cumulative/name/time/file sorting options.Jiri– Jiri02/25/2009 17:41:40Commented Feb 25, 2009 at 17:41
-
31It is also worth noting that you can use the cProfile module from ipython using the magic function %prun (profile run). First import your module, and then call the main function with %prun: import euler048; %prun euler048.main()RussellStewart– RussellStewart03/31/2014 19:58:56Commented Mar 31, 2014 at 19:58
-
75For visualizing cProfile dumps (created by
python -m cProfile -o <out.profile> <script>
), RunSnakeRun, invoked asrunsnake <out.profile>
is invaluable.lily– lily05/05/2014 01:33:21Commented May 5, 2014 at 1:33 -
22@NeilG even for python 3,
cprofile
is still recommended overprofile
.trichoplax is on Codidact now– trichoplax is on Codidact now01/04/2015 02:43:05Commented Jan 4, 2015 at 2:43 -
55For visualizing cProfile dumps, RunSnakeRun hasn't been updated since 2011 and doesn't support python3. You should use snakeviz insteadGiacomo Pigani– Giacomo Pigani12/11/2017 09:48:22Commented Dec 11, 2017 at 9:48
A while ago I made pycallgraph
which generates a visualisation from your Python code. Edit: I've updated the example to work with 3.3, the latest release as of this writing.
After a pip install pycallgraph
and installing GraphViz you can run it from the command line:
pycallgraph graphviz -- ./mypythonscript.py
Or, you can profile particular parts of your code:
from pycallgraph import PyCallGraph
from pycallgraph.output import GraphvizOutput
with PyCallGraph(output=GraphvizOutput()):
code_to_profile()
Either of these will generate a pycallgraph.png
file similar to the image below:
enter image description here
-
63Are you coloring based on the amount of calls? If so, you should color based on time because the function with the most calls isn't always the one that takes the most time.red– red08/06/2013 12:21:37Commented Aug 6, 2013 at 12:21
-
32@red You can customise colours however you like, and even independently for each measurement. For example red for calls, blue for time, green for memory usage.gak– gak08/06/2013 22:18:00Commented Aug 6, 2013 at 22:18
-
2getting this error
Traceback (most recent call last): /pycallgraph.py", line 90, in generate output.done() File "/net_downloaded/pycallgraph-develop/pycallgraph/output/graphviz.py", line 94, in done source = self.generate() File "/net_downloaded/pycallgraph-develop/pycallgraph/output/graphviz.py", line 143, in generate indent_join.join(self.generate_attributes()), File "/net_downloaded/pycallgraph-develop/pycallgraph/output/graphviz.py", line 169, in generate_attributes section, self.attrs_from_dict(attrs), ValueError: zero length field name in format
Ciasto piekarz– Ciasto piekarz08/18/2014 11:39:27Commented Aug 18, 2014 at 11:39 -
6I updated this to mention that you need to install GraphViz for things to work as described. On Ubuntu this is just
sudo apt-get install graphviz
.mlissner– mlissner11/18/2015 17:55:41Commented Nov 18, 2015 at 17:55 -
17The github page states that this project is abandoned ... :(A. Rabus– A. Rabus11/12/2019 12:45:45Commented Nov 12, 2019 at 12:45
It's worth pointing out that using the profiler only works (by default) on the main thread, and you won't get any information from other threads if you use them. This can be a bit of a gotcha as it is completely unmentioned in the profiler documentation.
If you also want to profile threads, you'll want to look at the threading.setprofile()
function in the docs.
You could also create your own threading.Thread
subclass to do it:
class ProfiledThread(threading.Thread):
# Overrides threading.Thread.run()
def run(self):
profiler = cProfile.Profile()
try:
return profiler.runcall(threading.Thread.run, self)
finally:
profiler.dump_stats('myprofile-%d.profile' % (self.ident,))
and use that ProfiledThread
class instead of the standard one. It might give you more flexibility, but I'm not sure it's worth it, especially if you are using third-party code which wouldn't use your class.
-
1I don't see any reference to runcall in the documentation either. Giving a look at cProfile.py, I'm not sure why you use the threading.Thread.run function nor self as argument. I'd have expected to see a reference to another thread's run method here.PypeBros– PypeBros11/09/2011 11:14:06Commented Nov 9, 2011 at 11:14
-
It's not in the documentation, but it is in the module. See hg.python.org/cpython/file/6bf07db23445/Lib/cProfile.py#l140. That allows you to profile a specific function call, and in our case we want to profile the Thread's
target
function, which is what thethreading.Thread.run()
call executes. But as I said in the answer, it's probably not worth it to subclass Thread, since any third-party code won't use it, and to instead usethreading.setprofile()
.Joe Shaw– Joe Shaw11/09/2011 14:04:40Commented Nov 9, 2011 at 14:04 -
11wrapping the code with profiler.enable() and profiler.disable() seems to work quite well, too. That's basically what runcall do and it doesn't enforce any number of argument or similar things.PypeBros– PypeBros11/10/2011 10:58:17Commented Nov 10, 2011 at 10:58
-
1I combined my own stackoverflow.com/questions/10748118/… with ddaa.net/blog/python/lsprof-calltree and it kindof works ;!-)Dima Tisnek– Dima Tisnek07/11/2012 15:05:09Commented Jul 11, 2012 at 15:05
-
1Joe, do you know how the profiler plays with asyncio in Python 3.4?Nick Chammas– Nick Chammas06/17/2015 22:44:20Commented Jun 17, 2015 at 22:44
Simplest and quickest way to find where all the time is going.
1. pip install snakeviz
2. python -m cProfile -o temp.dat <PROGRAM>.py
3. snakeviz temp.dat
Draws a pie chart in a browser. Biggest piece is the problem function. Very simple.
-
See also zaxliu’s answer which provides a link to the tool and example output.Melebius– Melebius06/08/2020 09:03:51Commented Jun 8, 2020 at 9:03
-
2Using this on windows, created a bat script for pycharm integration, it works like a charm! Thank youAndrea– Andrea11/25/2020 16:04:09Commented Nov 25, 2020 at 16:04
The python wiki is a great page for profiling resources: http://wiki.python.org/moin/PythonSpeed/PerformanceTips#Profiling_Code
as is the python docs: http://docs.python.org/library/profile.html
as shown by Chris Lawlor cProfile is a great tool and can easily be used to print to the screen:
python -m cProfile -s time mine.py <args>
or to file:
python -m cProfile -o output.file mine.py <args>
PS> If you are using Ubuntu, make sure to install python-profile
apt-get install python-profiler
If you output to file you can get nice visualizations using the following tools
PyCallGraph : a tool to create call graph images
install:
pip install pycallgraph
run:
pycallgraph mine.py args
view:
gimp pycallgraph.png
You can use whatever you like to view the png file, I used gimp
Unfortunately I often get
dot: graph is too large for cairo-renderer bitmaps. Scaling by 0.257079 to fit
which makes my images unusably small. So I generally create svg files:
pycallgraph -f svg -o pycallgraph.svg mine.py <args>
PS> make sure to install graphviz (which provides the dot program):
pip install graphviz
Alternative Graphing using gprof2dot via @maxy / @quodlibetor :
pip install gprof2dot
python -m cProfile -o profile.pstats mine.py
gprof2dot -f pstats profile.pstats | dot -Tsvg -o mine.svg
-
15
-
2graphviz is also required if you are using OSXVaibhav Mishra– Vaibhav Mishra01/30/2014 12:26:35Commented Jan 30, 2014 at 12:26
-
Project was archived on github and appears to be no longer maintained. github.com/gak/pycallgraphdre-hh– dre-hh02/24/2021 10:40:24Commented Feb 24, 2021 at 10:40
-
pip install pycallgraph
gave meerror: subprocess-exited-with-error ... error in pycallgraph setup command: use_2to3 is invalid.
Bill– Bill06/24/2024 23:43:49Commented Jun 24, 2024 at 23:43
@Maxy's comment on this answer helped me out enough that I think it deserves its own answer: I already had cProfile-generated .pstats files and I didn't want to re-run things with pycallgraph, so I used gprof2dot, and got pretty svgs:
$ sudo apt-get install graphviz
$ git clone https://github.com/jrfonseca/gprof2dot
$ ln -s "$PWD"/gprof2dot/gprof2dot.py ~/bin
$ cd $PROJECT_DIR
$ gprof2dot.py -f pstats profile.pstats | dot -Tsvg -o callgraph.svg
and BLAM!
It uses dot (the same thing that pycallgraph uses) so output looks similar. I get the impression that gprof2dot loses less information though:
-
2Good approach, works really well as you can view SVG in Chrome etc and scale it up/down. Third line has typo, should be: ln -s
pwd
/gprof2dot/gprof2dot.py $HOME/bin (or use ln -s $PWD/gprof2dot/gprof2dot.py ~/bin in most shells - grave accent is taken as formatting in first version).RichVel– RichVel01/04/2013 14:24:03Commented Jan 4, 2013 at 14:24 -
3Ah, good point. I get
ln
's argument-order wrong almost every time.quodlibetor– quodlibetor01/04/2013 15:52:25Commented Jan 4, 2013 at 15:52 -
8the trick is to remember that ln and cp have the same argument order - think of it as 'copying file1 to file2 or dir2, but making a link'RichVel– RichVel01/04/2013 15:54:31Commented Jan 4, 2013 at 15:54
-
That makes sense, I think the use of "TARGET" in the manpage throws me.quodlibetor– quodlibetor01/04/2013 18:11:10Commented Jan 4, 2013 at 18:11
-
1Thanks @quodlibetor! On Win 10, depending on the conda or pip install, the command line editor might claim that
dot
is not recognizable. Setting a PATH for dot is not advisable e.g. as per github.com/ContinuumIO/anaconda-issues/issues/1666. One can use the full path of graphviz dot instead, e.g.: i)python -m cProfile -o profile.pstats main.py
ii)gprof2dot -f pstats profile.pstats | "C:\Program Files (x86)\Graphviz2.38\bin\dot.exe" -Tsvg -o gprof2dot_pstats.svg
.Sven Haile– Sven Haile05/22/2019 19:22:23Commented May 22, 2019 at 19:22
I ran into a handy tool called SnakeViz when researching this topic. SnakeViz is a web-based profiling visualization tool. It is very easy to install and use. The usual way I use it is to generate a stat file with %prun
and then do analysis in SnakeViz.
The main viz technique used is Sunburst chart as shown below, in which the hierarchy of function calls is arranged as layers of arcs and time info encoded in their angular widths.
The best thing is you can interact with the chart. For example, to zoom in one can click on an arc, and the arc and its descendants will be enlarged as a new sunburst to display more details.
-
3CodeCabbie's answer includes the (short) installation instructions, and shows how to (easily) use SnakeViz.Oren Milman– Oren Milman08/18/2019 09:32:47Commented Aug 18, 2019 at 9:32
-
Here I've read IMHO good guide how to use profiling for Python on jupyter notebook: towardsdatascience.com/speed-up-jupyter-notebooks-20716cbe2025Alex Martian– Alex Martian09/04/2019 13:13:29Commented Sep 4, 2019 at 13:13
cProfile
is great for profiling, while kcachegrind
is great for visualizing the results. The pyprof2calltree
in between handles the file conversion.
python -m cProfile -o script.profile script.py
pyprof2calltree -i script.profile -o script.calltree
kcachegrind script.calltree
Required system packages:
kcachegrind
(Linux),qcachegrind
(MacOs)
Setup on Ubuntu:
apt-get install kcachegrind
pip install pyprof2calltree
The result:
-
18Mac Users install
brew install qcachegrind
and substitude eachkcachegrind
withqcachegrind
in the description for successful profiling.Kevin Katzke– Kevin Katzke12/17/2017 17:58:32Commented Dec 17, 2017 at 17:58 -
1I had to do this to get it to work:
export QT_X11_NO_MITSHM=1
Yonatan Simson– Yonatan Simson12/04/2019 11:20:15Commented Dec 4, 2019 at 11:20 -
2Out of bunch of solutions listed here: this one worked best with large profile data.
gprof2dot
is not interactive and does not have the overall cpu time (only relative percentage)tuna
andsnakeviz
die on larger profile.pycallgraph
is archived and no longer maintaineddre-hh– dre-hh02/24/2021 10:38:37Commented Feb 24, 2021 at 10:38 -
2@YonatanSimson You probably run
kcachegrind
in a docker container, which doesn't share IPC with the host by default. Another way to fix that is to run the docker container with--ipc=host
.Maxim Egorushkin– Maxim Egorushkin05/26/2021 15:56:04Commented May 26, 2021 at 15:56
I recently created tuna for visualizing Python runtime and import profiles; this may be helpful here.
Install with
pip install tuna
Create a runtime profile
python3 -m cProfile -o program.prof yourfile.py
or an import profile (Python 3.7+ required)
python3 -X importprofile yourfile.py 2> import.log
Then just run tuna on the file
tuna program.prof
-
This was the first solution that worked well for me.MRule– MRule02/17/2023 09:47:04Commented Feb 17, 2023 at 9:47
-
1Kudos and thanks for creating
Tuna
. Among the solutions shown on this page, yours andpyinstrument
were the best in figuring out what was taking up the most time in my app.Nav– Nav06/19/2023 00:50:20Commented Jun 19, 2023 at 0:50
Also worth mentioning is the GUI cProfile dump viewer RunSnakeRun. It allows you to sort and select, thereby zooming in on the relevant parts of the program. The sizes of the rectangles in the picture is proportional to the time taken. If you mouse over a rectangle it highlights that call in the table and everywhere on the map. When you double-click on a rectangle it zooms in on that portion. It will show you who calls that portion and what that portion calls.
The descriptive information is very helpful. It shows you the code for that bit which can be helpful when you are dealing with built-in library calls. It tells you what file and what line to find the code.
Also want to point at that the OP said 'profiling' but it appears he meant 'timing'. Keep in mind programs will run slower when profiled.
enter image description here
-
1This screenshot is evidence that Engineers need to be taught at least one chapter on how to use colors in GUI's. I've worked with Interaction Designers who'd turn away in disgust, on seeing this splosh of random colors.Nav– Nav06/19/2023 00:38:26Commented Jun 19, 2023 at 0:38
pprofile
line_profiler
(already presented here) also inspired pprofile
, which is described as:
Line-granularity, thread-aware deterministic and statistic pure-python profiler
It provides line-granularity as line_profiler
, is pure Python, can be used as a standalone command or a module, and can even generate callgrind-format files that can be easily analyzed with [k|q]cachegrind
.
vprof
There is also vprof, a Python package described as:
[...] providing rich and interactive visualizations for various Python program characteristics such as running time and memory usage.
-
Haven't tried
pprofile
, but I'm upvotingvprof
. Its "code heatmap" mode is similar to the Matlab profiler. Currently, correct usage on Windows is not in the readme, but invprof
's GitHub issues:py -m vprof -c <config> <src>
root– root01/08/2022 02:51:55Commented Jan 8, 2022 at 2:51
A nice profiling module is the line_profiler (called using the script kernprof.py). It can be downloaded here.
My understanding is that cProfile only gives information about total time spent in each function. So individual lines of code are not timed. This is an issue in scientific computing since often one single line can take a lot of time. Also, as I remember, cProfile didn't catch the time I was spending in say numpy.dot.
-
Note that the original repository has been archived. The currently maintained version is here: github.com/pyutils/line_profilermirekphd– mirekphd04/20/2022 09:08:08Commented Apr 20, 2022 at 9:08
The terminal-only (and simplest) solution, in case all those fancy UI's fail to install or to run:
ignore cProfile
completely and replace it with pyinstrument
, that will collect and display the tree of calls right after execution.
Install:
$ pip install pyinstrument
Profile and display result:
$ python -m pyinstrument ./prog.py
Works with python2 and 3.
[EDIT] The documentation of the API, for profiling only a part of the code, can be found here.
-
3Thank you, I think your answer should be much higher :)erhosen– erhosen04/17/2022 11:13:43Commented Apr 17, 2022 at 11:13
-
This is exactly the thing I was looking for. Thank you.AutomaticHourglass– AutomaticHourglass02/18/2023 15:01:34Commented Feb 18, 2023 at 15:01
With a statistical profiler like austin, no instrumentation is required, meaning that you can get profiling data out of a Python application simply with
austin python3 my_script.py
The raw output isn't very useful, but you can pipe that to flamegraph.pl to get a flame graph representation of that data that gives you a breakdown of where the time (measured in microseconds of real time) is being spent.
austin python3 my_script.py | flamegraph.pl > my_script_profile.svg
Alternatively, you can also use the web application Speedscope.app for quick visualisation of the collected samples. If you have pprof installed, you can also get austin-python (with e.g. pipx install austin-python
) and use the austin2pprof
to covert to the pprof format.
However, if you have VS Code installed you could use the Austin extension for a more interactive experience, with source code heat maps, top functions and collected call stacks
Austin VS Code extension
If you'd rather use the terminal, you can also use the TUI, that also has a live graph mode:
Austin TUI graph mode
There's a lot of great answers but they either use command line or some external program for profiling and/or sorting the results.
I really missed some way I could use in my IDE (eclipse-PyDev) without touching the command line or installing anything. So here it is.
Profiling without command line
def count():
from math import sqrt
for x in range(10**5):
sqrt(x)
if __name__ == '__main__':
import cProfile, pstats
cProfile.run("count()", "{}.profile".format(__file__))
s = pstats.Stats("{}.profile".format(__file__))
s.strip_dirs()
s.sort_stats("time").print_stats(10)
See docs or other answers for more info.
-
for example, the profile prints {map} or {xxx} . how do I know the method {xxx} is called from which file? my profile prints {method 'compress' of 'zlib.Compress' objects} takes most of time, but I don't use any zlib , so I guess some call numpy function may use it . How do I know which is the exactly file and line takes much time?machen– machen10/28/2017 13:05:32Commented Oct 28, 2017 at 13:05
-
This isn't fair... I dunno why this great answer has so few upvotes... much more useful than the other high-upvoted ones :/BPL– BPL07/16/2020 07:50:56Commented Jul 16, 2020 at 7:50
Following Joe Shaw's answer about multi-threaded code not to work as expected, I figured that the runcall
method in cProfile is merely doing self.enable()
and self.disable()
calls around the profiled function call, so you can simply do that yourself and have whatever code you want in-between with minimal interference with existing code.
-
3Excellent tip! A quick peek at
cprofile.py
's source code reveals that's exactly whatruncall()
does. Being more specific, after creating a Profile instance withprof = cprofile.Profile()
, immediately callprof.disable()
, and then just addprof.enable()
andprof.disable()
calls around the section of code you want profiled.martineau– martineau10/21/2012 21:39:47Commented Oct 21, 2012 at 21:39 -
This is very helpful, but it seems the code that is actually between enable and disable is not profiled -- only the functions it calls. Do I have this right? I'd have to wrap that code in a function call for it to count toward any of the numbers in print_stats().Bob Stein– Bob Stein05/09/2017 13:18:30Commented May 9, 2017 at 13:18
For getting quick profile stats on an IPython notebook. One can embed line_profiler and memory_profiler straight into their notebooks.
Another useful package is Pympler. It is a powerful profiling package that's capable to track classes,objects,functions,memory leaks etc. Examples below, Docs attached.
Get it!
!pip install line_profiler
!pip install memory_profiler
!pip install pympler
Load it!
%load_ext line_profiler
%load_ext memory_profiler
Use it!
%time
%time print('Outputs CPU time,Wall Clock time')
#CPU times: user 2 μs, sys: 0 ns, total: 2 μs Wall time: 5.96 μs
Gives:
- CPU times: CPU level execution time
- sys times: system level execution time
- total: CPU time + system time
- Wall time: Wall Clock Time
%timeit
%timeit -r 7 -n 1000 print('Outputs execution time of the snippet')
#1000 loops, best of 7: 7.46 ns per loop
- Gives best time out of given number of runs(r) in looping (n) times.
- Outputs details on system caching:
- When code snippets are executed multiple times, system caches a few opearations and doesn't execute them again that may hamper the accuracy of the profile reports.
%prun
%prun -s cumulative 'Code to profile'
Gives:
- number of function calls(ncalls)
- has entries per function call(distinct)
- time taken per call(percall)
- time elapsed till that function call(cumtime)
- name of the func/module called etc...
%memit
%memit 'Code to profile'
#peak memory: 199.45 MiB, increment: 0.00 MiB
Gives:
- Memory usage
%lprun
#Example function
def fun():
for i in range(10):
print(i)
#Usage: %lprun <name_of_the_function> function
%lprun -f fun fun()
Gives:
- Line wise stats
sys.getsizeof
sys.getsizeof('code to profile')
# 64 bytes
Returns the size of an object in bytes.
asizeof() from pympler
from pympler import asizeof
obj = [1,2,("hey","ha"),3]
print(asizeof.asizeof(obj,stats=4))
pympler.asizeof can be used to investigate how much memory certain Python objects consume. In contrast to sys.getsizeof, asizeof sizes objects recursively
tracker from pympler
from pympler import tracker
tr = tracker.SummaryTracker()
def fun():
li = [1,2,3]
di = {"ha":"haha","duh":"Umm"}
fun()
tr.print_diff()
Tracks the lifetime of a function.
Pympler package consists of a huge number of high utility functions to profile code. All of which cannot be covered here. See the documentation attached for verbose profile implementations.
Pympler doc
Recently I created a plugin for PyCharm with which you can easily analyse and visualise the results of line_profiler
in the PyCharm editor.
line_profiler
has been mentioned in other answers as well and is a great tool to analyse exactly how much time is spent by the python interpreter in certain lines.
The PyCharm plugin I've created can be found here: https://plugins.jetbrains.com/plugin/16536-line-profiler
It needs a helper package in your python environment called line-profiler-pycharm
which can be installed with pip or by the plugin itself.
After installing the plugin in PyCharm:
- Decorate any function you want to profile with the
line_profiler_pycharm.profile
decorator - Run with the 'Profile Lines' runner
Screenshot of results: Line Profiler Pycharm results
-
1
-
thanks for this tool, it's great!birnbaum– birnbaum07/23/2024 10:36:13Commented Jul 23, 2024 at 10:36
In Virtaal's source there's a very useful class and decorator that can make profiling (even for specific methods/functions) very easy. The output can then be viewed very comfortably in KCacheGrind.
-
1Thank you for this gem. FYI: This can be used as a standalone module with any code, Virtaal code base is not required. Just save the file to profiling.py and import the profile_func(). Use @profile_func() as a decorator to any function you need to profile and viola. :)Amjith– Amjith10/06/2011 05:23:09Commented Oct 6, 2011 at 5:23
If you want to make a cumulative profiler, meaning to run the function several times in a row and watch the sum of the results.
you can use this cumulative_profiler
decorator:
it's python>= 3.6 specific, but you can remove nonlocal
for it work on older versions.
import cProfile, pstats
class _ProfileFunc:
def __init__(self, func, sort_stats_by):
self.func = func
self.profile_runs = []
self.sort_stats_by = sort_stats_by
def __call__(self, *args, **kwargs):
pr = cProfile.Profile()
pr.enable() # this is the profiling section
retval = self.func(*args, **kwargs)
pr.disable()
self.profile_runs.append(pr)
ps = pstats.Stats(*self.profile_runs).sort_stats(self.sort_stats_by)
return retval, ps
def cumulative_profiler(amount_of_times, sort_stats_by='time'):
def real_decorator(function):
def wrapper(*args, **kwargs):
nonlocal function, amount_of_times, sort_stats_by # for python 2.x remove this row
profiled_func = _ProfileFunc(function, sort_stats_by)
for i in range(amount_of_times):
retval, ps = profiled_func(*args, **kwargs)
ps.print_stats()
return retval # returns the results of the function
return wrapper
if callable(amount_of_times): # incase you don't want to specify the amount of times
func = amount_of_times # amount_of_times is the function in here
amount_of_times = 5 # the default amount
return real_decorator(func)
return real_decorator
Example
profiling the function baz
import time
@cumulative_profiler
def baz():
time.sleep(1)
time.sleep(2)
return 1
baz()
baz
ran 5 times and printed this:
20 function calls in 15.003 seconds
Ordered by: internal time
ncalls tottime percall cumtime percall filename:lineno(function)
10 15.003 1.500 15.003 1.500 {built-in method time.sleep}
5 0.000 0.000 15.003 3.001 <ipython-input-9-c89afe010372>:3(baz)
5 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects}
specifying the amount of times
@cumulative_profiler(3)
def baz():
...
cProfile is great for quick profiling but most of the time it was ending for me with the errors. Function runctx solves this problem by initializing correctly the environment and variables, hope it can be useful for someone:
import cProfile
cProfile.runctx('foo()', None, locals())
gprof2dot_magic
Magic function for gprof2dot
to profile any Python statement as a DOT graph in JupyterLab or Jupyter Notebook.
GitHub repo: https://github.com/mattijn/gprof2dot_magic
installation
Make sure you've the Python package gprof2dot_magic
.
pip install gprof2dot_magic
Its dependencies gprof2dot
and graphviz
will be installed as well
usage
To enable the magic function, first load the gprof2dot_magic
module
%load_ext gprof2dot_magic
and then profile any line statement as a DOT graph as such:
%gprof2dot print('hello world')
I just developed my own profiler inspired from pypref_time:
https://github.com/modaresimr/auto_profiler
Update Version 2
Install:
pip install auto_profiler
Quick Start:
from auto_profiler import Profiler
with Profiler():
your_function()
Using in Jupyter, let you have realtime view of elapsed times
Real Time view of auto profiler in jupyter
Update Version 1
By adding a decorator it will show a tree of time-consuming functions
@Profiler(depth=4)
Install by: pip install auto_profiler
Example
import time # line number 1
import random
from auto_profiler import Profiler, Tree
def f1():
mysleep(.6+random.random())
def mysleep(t):
time.sleep(t)
def fact(i):
f1()
if(i==1):
return 1
return i*fact(i-1)
def main():
for i in range(5):
f1()
fact(3)
with Profiler(depth=4):
main()
Example Output
Time [Hits * PerHit] Function name [Called from] [function location]
-----------------------------------------------------------------------
8.974s [1 * 8.974] main [auto-profiler/profiler.py:267] [/test/t2.py:30]
├── 5.954s [5 * 1.191] f1 [/test/t2.py:34] [/test/t2.py:14]
│ └── 5.954s [5 * 1.191] mysleep [/test/t2.py:15] [/test/t2.py:17]
│ └── 5.954s [5 * 1.191] <time.sleep>
|
|
| # The rest is for the example recursive function call fact
└── 3.020s [1 * 3.020] fact [/test/t2.py:36] [/test/t2.py:20]
├── 0.849s [1 * 0.849] f1 [/test/t2.py:21] [/test/t2.py:14]
│ └── 0.849s [1 * 0.849] mysleep [/test/t2.py:15] [/test/t2.py:17]
│ └── 0.849s [1 * 0.849] <time.sleep>
└── 2.171s [1 * 2.171] fact [/test/t2.py:24] [/test/t2.py:20]
├── 1.552s [1 * 1.552] f1 [/test/t2.py:21] [/test/t2.py:14]
│ └── 1.552s [1 * 1.552] mysleep [/test/t2.py:15] [/test/t2.py:17]
└── 0.619s [1 * 0.619] fact [/test/t2.py:24] [/test/t2.py:20]
└── 0.619s [1 * 0.619] f1 [/test/t2.py:21] [/test/t2.py:14]
-
Wow, for such a cool profiler, why not more stars on GitHub?Dan Nissenbaum– Dan Nissenbaum09/23/2021 18:45:39Commented Sep 23, 2021 at 18:45
-
@Dan Nissenbaum, I'm so happy to hear your interest. I don't have a big network so no one knows this tool. I hope I can continue maintaining this project by hearing such interests😊Ali– Ali09/24/2021 21:33:27Commented Sep 24, 2021 at 21:33
My way is to use yappi (https://github.com/sumerc/yappi). It's especially useful combined with an RPC server where (even just for debugging) you register method to start, stop and print profiling information, e.g. in this way:
@staticmethod
def startProfiler():
yappi.start()
@staticmethod
def stopProfiler():
yappi.stop()
@staticmethod
def printProfiler():
stats = yappi.get_stats(yappi.SORTTYPE_TTOT, yappi.SORTORDER_DESC, 20)
statPrint = '\n'
namesArr = [len(str(stat[0])) for stat in stats.func_stats]
log.debug("namesArr %s", str(namesArr))
maxNameLen = max(namesArr)
log.debug("maxNameLen: %s", maxNameLen)
for stat in stats.func_stats:
nameAppendSpaces = [' ' for i in range(maxNameLen - len(stat[0]))]
log.debug('nameAppendSpaces: %s', nameAppendSpaces)
blankSpace = ''
for space in nameAppendSpaces:
blankSpace += space
log.debug("adding spaces: %s", len(nameAppendSpaces))
statPrint = statPrint + str(stat[0]) + blankSpace + " " + str(stat[1]).ljust(8) + "\t" + str(
round(stat[2], 2)).ljust(8 - len(str(stat[2]))) + "\t" + str(round(stat[3], 2)) + "\n"
log.log(1000, "\nname" + ''.ljust(maxNameLen - 4) + " ncall \tttot \ttsub")
log.log(1000, statPrint)
Then when your program work you can start profiler at any time by calling the startProfiler
RPC method and dump profiling information to a log file by calling printProfiler
(or modify the rpc method to return it to the caller) and get such output:
2014年02月19日 16:32:24,128-|SVR-MAIN |-(Thread-3 )-Level 1000:
name ncall ttot tsub
2014年02月19日 16:32:24,128-|SVR-MAIN |-(Thread-3 )-Level 1000:
C:\Python27\lib\sched.py.run:80 22 0.11 0.05
M:02円_documents\_repos09円_aheadRepos\apps\ahdModbusSrv\pyAheadRpcSrv\xmlRpc.py.iterFnc:293 22 0.11 0.0
M:02円_documents\_repos09円_aheadRepos\apps\ahdModbusSrv\serverMain.py.makeIteration:515 22 0.11 0.0
M:02円_documents\_repos09円_aheadRepos\apps\ahdModbusSrv\pyAheadRpcSrv\PicklingXMLRPC.py._dispatch:66 1 0.0 0.0
C:\Python27\lib\BaseHTTPServer.py.date_time_string:464 1 0.0 0.0
c:\users\zasiec~1\appdata\local\temp\easy_install-hwcsr1\psutil-1.1.2-py2.7-win32.egg.tmp\psutil\_psmswindows.py._get_raw_meminfo:243 4 0.0 0.0
C:\Python27\lib\SimpleXMLRPCServer.py.decode_request_content:537 1 0.0 0.0
c:\users\zasiec~1\appdata\local\temp\easy_install-hwcsr1\psutil-1.1.2-py2.7-win32.egg.tmp\psutil\_psmswindows.py.get_system_cpu_times:148 4 0.0 0.0
<string>.__new__:8 220 0.0 0.0
C:\Python27\lib\socket.py.close:276 4 0.0 0.0
C:\Python27\lib\threading.py.__init__:558 1 0.0 0.0
<string>.__new__:8 4 0.0 0.0
C:\Python27\lib\threading.py.notify:372 1 0.0 0.0
C:\Python27\lib\rfc822.py.getheader:285 4 0.0 0.0
C:\Python27\lib\BaseHTTPServer.py.handle_one_request:301 1 0.0 0.0
C:\Python27\lib\xmlrpclib.py.end:816 3 0.0 0.0
C:\Python27\lib\SimpleXMLRPCServer.py.do_POST:467 1 0.0 0.0
C:\Python27\lib\SimpleXMLRPCServer.py.is_rpc_path_valid:460 1 0.0 0.0
C:\Python27\lib\SocketServer.py.close_request:475 1 0.0 0.0
c:\users\zasiec~1\appdata\local\temp\easy_install-hwcsr1\psutil-1.1.2-py2.7-win32.egg.tmp\psutil\__init__.py.cpu_times:1066 4 0.0 0.0
It may not be very useful for short scripts but helps to optimize server-type processes especially given the printProfiler
method can be called multiple times over time to profile and compare e.g. different program usage scenarios.
In newer versions of yappi, the following code will work:
@staticmethod
def printProfile():
yappi.get_func_stats().print_all()
-
Shouldn't it be named the Stupendous Yappi?Therealstubot– Therealstubot07/11/2014 22:43:15Commented Jul 11, 2014 at 22:43
-
Unfortunately the code above works only with version 0.62 which is not available on pypy. Module needs to be compiled from 0.62 sources available here: github.com/nirs/yappi/releases or use build I made for windows in repo forked for that purpose github.com/Girgitt/yappi/releasesMr. Girgitt– Mr. Girgitt03/26/2018 15:33:49Commented Mar 26, 2018 at 15:33
-
compatibility with version 1.0 can be easily provided - at least for print output - by modifying the printProfiler function:
def printProfiler(): if not yappi_available: return stats = yappi.get_func_stats() stats.print_all(columns={0:("name",90), 1:("ncall", 5), 2:("tsub", 8), 3:("ttot", 8), 4:("tavg",8)})
(OK after trying couple times to insert code block into the comment I gave up. this is unbelievably difficult for a programming-oriented Q&A site.)Mr. Girgitt– Mr. Girgitt11/26/2019 20:12:40Commented Nov 26, 2019 at 20:12
Scalene is a new python profiler that covers many use cases and has a minimal performance impact:
https://github.com/plasma-umass/scalene
It can profile CPU, GPU and memory utilisation at a very granular level. It also notably supports multi-threaded / parallelized python code.
A new tool to handle profiling in Python is PyVmMonitor: http://www.pyvmmonitor.com/
It has some unique features such as
- Attach profiler to a running (CPython) program
- On demand profiling with Yappi integration
- Profile on a different machine
- Multiple processes support (multiprocessing, django...)
- Live sampling/CPU view (with time range selection)
- Deterministic profiling through cProfile/profile integration
- Analyze existing PStats results
- Open DOT files
- Programatic API access
- Group samples by method or line
- PyDev integration
- PyCharm integration
Note: it's commercial, but free for open source.
To add on to https://stackoverflow.com/a/582337/1070617,
I wrote this module that allows you to use cProfile and view its output easily. More here: https://github.com/ymichael/cprofilev
$ python -m cprofilev /your/python/program
# Go to http://localhost:4000 to view collected statistics.
Also see: http://ymichael.com/2014/03/08/profiling-python-with-cprofile.html on how to make sense of the collected statistics.
It would depend on what you want to see out of profiling. Simple time metrics can be given by (bash).
time python python_prog.py
Even '/usr/bin/time' can output detailed metrics by using '--verbose' flag.
To check time metrics given by each function and to better understand how much time is spent on functions, you can use the inbuilt cProfile in python.
Going into more detailed metrics like performance, time is not the only metric. You can worry about memory, threads etc.
Profiling options:
1. line_profiler is another profiler used commonly to find out timing metrics line-by-line.
2. memory_profiler is a tool to profile memory usage.
3. heapy (from project Guppy) Profile how objects in the heap are used.
These are some of the common ones I tend to use. But if you want to find out more, try reading this book It is a pretty good book on starting out with performance in mind. You can move onto advanced topics on using Cython and JIT(Just-in-time) compiled python.
Ever want to know what the hell that python script is doing? Enter the Inspect Shell. Inspect Shell lets you print/alter globals and run functions without interrupting the running script. Now with auto-complete and command history (only on linux).
Inspect Shell is not a pdb-style debugger.
https://github.com/amoffat/Inspect-Shell
You could use that (and your wristwatch).
There's also a statistical profiler called statprof
. It's a sampling profiler, so it adds minimal overhead to your code and gives line-based (not just function-based) timings. It's more suited to soft real-time applications like games, but may be have less precision than cProfile.
The version in pypi is a bit old, so can install it with pip
by specifying the git repository:
pip install git+git://github.com/bos/statprof.py@1a33eba91899afe17a8b752c6dfdec6f05dd0c01
You can run it like this:
import statprof
with statprof.profile():
my_questionable_function()
Explore related questions
See similar questions with these tags.
line-profiler
, for line-by-line profiling