{"id":194,"date":"2015-08-31T03:27:25","date_gmt":"2015-08-30T19:27:25","guid":{"rendered":"https:\/\/vinta.ws\/code\/?p=194"},"modified":"2026-02-18T01:20:36","modified_gmt":"2026-02-17T17:20:36","slug":"tools-for-profiling-your-python-django-project","status":"publish","type":"post","link":"https:\/\/vinta.ws\/code\/tools-for-profiling-your-python-django-project.html","title":{"rendered":"Tools for Profiling your Python Projects"},"content":{"rendered":"<p>The first aim of profiling is to test a representative system to identify what's slow, using too much RAM, causing too much disk I\/O or network I\/O. You should keep in mind that profiling typically adds an overhead to your code.<\/p>\n<p>In this post, I will introduce tools you could use to profile your Python or Django projects, including: <code>timer<\/code>, <code>pycallgraph<\/code>, <code>cProfile<\/code>, <code>line-profiler<\/code>, <code>memory-profiler<\/code>.<\/p>\n<p>ref:<br \/>\n<a href=\"https:\/\/stackoverflow.com\/questions\/582336\/how-can-you-profile-a-script\">https:\/\/stackoverflow.com\/questions\/582336\/how-can-you-profile-a-script<\/a><br \/>\n<a href=\"https:\/\/www.airpair.com\/python\/posts\/optimizing-python-code\">https:\/\/www.airpair.com\/python\/posts\/optimizing-python-code<\/a><\/p>\n<h2>timer<\/h2>\n<p>The simplest way to profile a piece of code.<\/p>\n<p>ref:<br \/>\n<a href=\"https:\/\/docs.python.org\/3\/library\/timeit.html\">https:\/\/docs.python.org\/3\/library\/timeit.html<\/a><\/p>\n<h2>pycallgraph<\/h2>\n<p><code>pycallgraph<\/code> is a Python module that creates call graph visualizations for Python applications.<\/p>\n<p>ref:<br \/>\n<a href=\"https:\/\/pycallgraph.readthedocs.org\/en\/latest\/\">https:\/\/pycallgraph.readthedocs.org\/en\/latest\/<\/a><\/p>\n<pre class=\"line-numbers\"><code class=\"language-console\">$ sudo apt-get install graphviz\n$ pip install pycallgraph<\/code><\/pre>\n<pre class=\"line-numbers\"><code class=\"language-py\"># in your_app\/middlewares.py\nfrom pycallgraph import Config\nfrom pycallgraph import PyCallGraph\nfrom pycallgraph.globbing_filter import GlobbingFilter\nfrom pycallgraph.output import GraphvizOutput\nimport time\n\nclass PyCallGraphMiddleware(object):\n\n    def process_view(self, request, callback, callback_args, callback_kwargs):\n        if 'graph' in request.GET:\n            config = Config()\n            config.trace_filter = GlobbingFilter(include=['rest_framework.*', 'api.*', 'music.*'])\n            graphviz = GraphvizOutput(output_file='pycallgraph-{}.png'.format(time.time()))\n            pycallgraph = PyCallGraph(output=graphviz, config=config)\n            pycallgraph.start()\n\n            self.pycallgraph = pycallgraph\n\n    def process_response(self, request, response):\n        if 'graph' in request.GET:\n            self.pycallgraph.done()\n\n        return response<\/code><\/pre>\n<pre class=\"line-numbers\"><code class=\"language-py\"># in settings.py\nMIDDLEWARE_CLASSES = (\n    'your_app.middlewares.PyCallGraphMiddleware',\n    ...\n)<\/code><\/pre>\n<pre class=\"line-numbers\"><code class=\"language-console\">$ python manage.py runserver 0.0.0.0:8000\n$ open http:\/\/127.0.0.1:8000\/your_endpoint\/?graph=true<\/code><\/pre>\n<h2>cProfile<\/h2>\n<p><code>cProfile<\/code> is a tool in Python's standard library to understand which functions in your code take the longest to run. It will give you a high-level view of the performance problem so you can direct your attention to the critical functions.<\/p>\n<p>ref:<br \/>\n<a href=\"http:\/\/igor.kupczynski.info\/2015\/01\/16\/profiling-python-scripts.html\">http:\/\/igor.kupczynski.info\/2015\/01\/16\/profiling-python-scripts.html<\/a><br \/>\n<a href=\"https:\/\/ymichael.com\/2014\/03\/08\/profiling-python-with-cprofile.html\">https:\/\/ymichael.com\/2014\/03\/08\/profiling-python-with-cprofile.html<\/a><\/p>\n<pre class=\"line-numbers\"><code class=\"language-console\">$ python -m cProfile manage.py test member\n$ python -m cProfile -o my-profile-data.out manage.py test --failtest\n$ python -m cProfile -o my-profile-data.out manage.py runserver 0.0.0.0:8000\n\n$ pip install cprofilev\n$ cprofilev -f my-profile-data.out -a 0.0.0.0 -p 4000\n$ open http:\/\/127.0.0.1:4000<\/code><\/pre>\n<h3>cProfile with django-cprofile-middleware<\/h3>\n<pre class=\"line-numbers\"><code class=\"language-console\">$ pip install django-cprofile-middleware<\/code><\/pre>\n<pre class=\"line-numbers\"><code class=\"language-py\"># in settings.py\nMIDDLEWARE_CLASSES = (\n    ...\n    'django_cprofile_middleware.middleware.ProfilerMiddleware',\n)<\/code><\/pre>\n<p>Open any url with a <code>?prof<\/code> suffix to do the profiling, for instance, <a href=\"http:\/\/localhost:8000\/foo\/?prof\">http:\/\/localhost:8000\/foo\/?prof<\/a><\/p>\n<p>ref:<br \/>\n<a href=\"https:\/\/github.com\/omarish\/django-cprofile-middleware\">https:\/\/github.com\/omarish\/django-cprofile-middleware<\/a><\/p>\n<h3>cProfile with django-extension and kcachegrind<\/h3>\n<p><code>kcachegrind<\/code> is a profiling data visualization tool, used to determine the most time consuming execution parts of a program.<\/p>\n<p>ref:<br \/>\n<a href=\"http:\/\/django-extensions.readthedocs.org\/en\/latest\/runprofileserver.html\">http:\/\/django-extensions.readthedocs.org\/en\/latest\/runprofileserver.html<\/a><\/p>\n<pre class=\"line-numbers\"><code class=\"language-console\">$ pip install django-extensions<\/code><\/pre>\n<pre class=\"line-numbers\"><code class=\"language-py\"># in settings.py\nINSTALLED_APPS += (\n    'django_extensions',\n)<\/code><\/pre>\n<pre class=\"line-numbers\"><code class=\"language-console\">$ mkdir -p my-profile-data\n\n$ python manage.py runprofileserver \n--noreload \n--nomedia \n--nostatic \n--kcachegrind \n--prof-path=my-profile-data \n0.0.0.0:8000\n\n$ brew install qcachegrind --with-graphviz\n$ qcachegrind my-profile-data\/root.003563ms.1441992439.prof\n# or\n$ sudo apt-get install kcachegrind\n$ kcachegrind my-profile-data\/root.003563ms.1441992439.prof<\/code><\/pre>\n<h3>cProfile with django-debug-toolbar<\/h3>\n<p>You're only able to use <code>django-debug-toolbar<\/code> if your view returns HTML, it needs a place to inject the debug panels into your DOM on the webpage.<\/p>\n<p>ref:<br \/>\n<a href=\"https:\/\/github.com\/django-debug-toolbar\/django-debug-toolbar\">https:\/\/github.com\/django-debug-toolbar\/django-debug-toolbar<\/a><\/p>\n<pre class=\"line-numbers\"><code class=\"language-console\">$ pip install django-debug-toolbar<\/code><\/pre>\n<pre class=\"line-numbers\"><code class=\"language-py\"># in settiangs.py\nINSTALLED_APPS += (\n    'debug_toolbar',\n)\n\nDEBUG_TOOLBAR_PANELS = [\n    ...\n    'debug_toolbar.panels.profiling.ProfilingPanel',\n    ...\n]<\/code><\/pre>\n<h2>line-profiler<\/h2>\n<p><code>line-profiler<\/code> is a module for doing line-by-line profiling of functions. One of my favorite tools.<\/p>\n<p>ref:<br \/>\n<a href=\"https:\/\/github.com\/rkern\/line_profiler\">https:\/\/github.com\/rkern\/line_profiler<\/a><\/p>\n<pre class=\"line-numbers\"><code class=\"language-console\">$ pip install line-profiler<\/code><\/pre>\n<pre class=\"line-numbers\"><code class=\"language-py\"># in your_app\/views.py\ndef do_line_profiler(view=None, extra_view=None):\n    import line_profiler\n\n    def wrapper(view):\n        def wrapped(*args, **kwargs):\n            prof = line_profiler.LineProfiler()\n            prof.add_function(view)\n            if extra_view:\n                [prof.add_function(v) for v in extra_view]\n            with prof:\n                resp = view(*args, **kwargs)\n            prof.print_stats()\n            return resp\n\n        return wrapped\n\n    if view:\n        return wrapper(view)\n\n    return wrapper\n\n@do_line_profiler\ndef your_view(request):\n    pass<\/code><\/pre>\n<p>ref:<br \/>\n<a href=\"https:\/\/djangosnippets.org\/snippets\/10483\/\">https:\/\/djangosnippets.org\/snippets\/10483\/<\/a><\/p>\n<p>There is a pure Python alternative: <code>pprofile<\/code>.<br \/>\n<a href=\"https:\/\/github.com\/vpelletier\/pprofile\">https:\/\/github.com\/vpelletier\/pprofile<\/a><\/p>\n<h3>line-profiler with django-devserver<\/h3>\n<p>ref:<br \/>\n<a href=\"https:\/\/github.com\/dcramer\/django-devserver\">https:\/\/github.com\/dcramer\/django-devserver<\/a><\/p>\n<pre class=\"line-numbers\"><code class=\"language-console\">$ pip install git+git:\/\/github.com\/dcramer\/django-devserver#egg=django-devserver<\/code><\/pre>\n<p>in settings.py<\/p>\n<pre class=\"line-numbers\"><code class=\"language-py\">INSTALLED_APPS += (\n    'devserver',\n)\n\nDEVSERVER_MODULES = (\n    ...\n    'devserver.modules.profile.LineProfilerModule',\n    ...\n)\n\nDEVSERVER_AUTO_PROFILE = False<\/code><\/pre>\n<p>in your_app\/views.py<\/p>\n<pre class=\"line-numbers\"><code class=\"language-py\">from devserver.modules.profile import devserver_profile\n\n@devserver_profile()\ndef your_view(request):\n    pass<\/code><\/pre>\n<h3>line-profiler with django-debug-toolbar-line-profiler<\/h3>\n<p>ref:<br \/>\n<a href=\"http:\/\/django-debug-toolbar.readthedocs.org\/en\/latest\/\">http:\/\/django-debug-toolbar.readthedocs.org\/en\/latest\/<\/a><br \/>\n<a href=\"https:\/\/github.com\/dmclain\/django-debug-toolbar-line-profiler\">https:\/\/github.com\/dmclain\/django-debug-toolbar-line-profiler<\/a><\/p>\n<pre class=\"line-numbers\"><code class=\"language-console\">$ pip install django-debug-toolbar django-debug-toolbar-line-profiler<\/code><\/pre>\n<pre class=\"line-numbers\"><code class=\"language-py\"># in settings.py\nINSTALLED_APPS += (\n    'debug_toolbar',\n    'debug_toolbar_line_profiler',\n)\n\nDEBUG_TOOLBAR_PANELS = [\n    ...\n    'debug_toolbar_line_profiler.panel.ProfilingPanel',\n    ...\n]<\/code><\/pre>\n<h2>memory-profiler<\/h2>\n<p>This is a Python module for monitoring memory consumption of a process as well as line-by-line analysis of memory consumption for Python programs.<\/p>\n<p>ref:<br \/>\n<a href=\"https:\/\/pypi.python.org\/pypi\/memory_profiler\">https:\/\/pypi.python.org\/pypi\/memory_profiler<\/a><\/p>\n<pre class=\"line-numbers\"><code class=\"language-console\">$ pip install memory-profiler psutil<\/code><\/pre>\n<pre class=\"line-numbers\"><code class=\"language-py\"># in your_app\/views.py\nfrom memory_profiler import profile\n\n@profile(precision=4)\ndef your_view(request):\n    pass<\/code><\/pre>\n<p>There are other options:<br \/>\n<a href=\"http:\/\/stackoverflow.com\/questions\/110259\/which-python-memory-profiler-is-recommended\">http:\/\/stackoverflow.com\/questions\/110259\/which-python-memory-profiler-is-recommended<\/a><\/p>\n<h2>dogslow<\/h2>\n<p>ref:<br \/>\n<a href=\"https:\/\/bitbucket.org\/evzijst\/dogslow\">https:\/\/bitbucket.org\/evzijst\/dogslow<\/a><\/p>\n<h2>django-slow-tests<\/h2>\n<p>ref:<br \/>\n<a href=\"https:\/\/github.com\/realpython\/django-slow-tests\">https:\/\/github.com\/realpython\/django-slow-tests<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Tools you could use to profile your Python, Django or Flask projects, including: timer, pycallgraph, cProfile, line-profiler, memory-profiler.<\/p>\n","protected":false},"author":1,"featured_media":294,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[4,116],"tags":[29,13,81,2],"class_list":["post-194","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-about-python","category-about-web-development","tag-debug","tag-django","tag-profiling","tag-python"],"_links":{"self":[{"href":"https:\/\/vinta.ws\/code\/wp-json\/wp\/v2\/posts\/194","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/vinta.ws\/code\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/vinta.ws\/code\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/vinta.ws\/code\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/vinta.ws\/code\/wp-json\/wp\/v2\/comments?post=194"}],"version-history":[{"count":0,"href":"https:\/\/vinta.ws\/code\/wp-json\/wp\/v2\/posts\/194\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/vinta.ws\/code\/wp-json\/wp\/v2\/media\/294"}],"wp:attachment":[{"href":"https:\/\/vinta.ws\/code\/wp-json\/wp\/v2\/media?parent=194"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/vinta.ws\/code\/wp-json\/wp\/v2\/categories?post=194"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/vinta.ws\/code\/wp-json\/wp\/v2\/tags?post=194"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}