How to Profile a Python Program

Bilal Özdemir
blutv
Published in
4 min readApr 5, 2021

Python is designed to accelerate software development. It allows you to build your dream project swiftly. Mostly you will get enough performance from Python, however, you can expect a decent amount of decrease in performance when your project gets larger and complicated. Therefore, you will probably encounter scaling problems and large costs at some point. It’s okay though. After you manage to complete your project, you can start to apply optimizations.

Optimizations in a Python code base can be applied relatively easier than other programming languages. In short, you can write code and optimize that code rapidly with Python. Firstly, you need to understand which parts of your code need to be optimized, whether an aspect of execution times or usage of system resources. You can improve your code quality by optimizing your code.

Let’s go through some examples of profiling Python code.

TL;DR

1. Profiling Execution Times

You know that there is always more than one way to implement an idea, an algorithm, or a solution, you name it. And you may be stuck thinking which method would be better to implement in the aspect of performance. Or you might be indecisive about which library to use amongst countless numbers of others. cProfile and timeit modules are here to save you from your endless thoughts and hesitations.

You can profile every part of your code, from simple functions to whole modules.

A simple comparison of two built-in functions:

Both functions call their own square root functions 10**5 (10000) times with BIG_NUMBER given. The results are (ran on Python 3.8.5):

╔═════════════════╦════════════════════╗
║ Function ║ Execution Time ║
╠═════════════════╬════════════════════╣
║ fast_function ║ 17ms ║
║ slow_function ║ 283ms ║
╚═════════════════╩════════════════════╝

As you can see from the results, using n**.5 rather than sqrt(n) will result in faster code.

Important note: This was just a random example. Please see ‘Pitfalls’ section below before making this kind of optimizations.

Profiling a script (example.py)

Run in terminal:

python -m cProfile example.py

Yes, that command yields a bunch of lines of data. Instead, you can use the timeit module to get plainer and comprehensible stats.

Quick note: the last two comparisons can return different results because we profiled a whole script at first, imports were included in the profile, and Python has a quite considerable amount of overhead for import. On the other hand, we just measured the time elapsed when processing the function get_word_count_of in the second profile.

So, these tools can give you insights on which parts of your code are working fast or slow and you can take an action depending on these insights. timeit and cProfile will meet your needs on profiling your code.

2. Profiling Memory Usage

Execution times put aside, memory usage is quite important too. You should track your memory usage (at least approximately) and perform heap analysis, so you can point out which parts of your code need to be optimized to get the most out of your available system resources. The more efficient usage of system resources, the more scalability and performance you’ll get.

There are two open-source tools that I can recommend:

It’s quite easy to use these tools, and there is enough guidance on their home page. Nevertheless, let’s go through some examples here as well.

memory_profiler has a decorator profile that monitors the wrapped function’s memory usage. You don’t need to change anything in your function. Just plug in the @profile expression on top of your function. As simple as that.

python -m memory_profiler example.py

As you can see in the result, using Generator iterable instead of List iterable decreased our memory usage by .5MiB.

You can reach more detailed information on memory_profiler homepage.

3. Using Scalene as a Profiler

Scalene is a full-featured profiler written in Python, C++, and C which makes it really faster than other profilers.

Profiling with Scalene is as simple as;

#pip install scalene
scalene example.py

Outputs as

We can see which code blocks take most of the time elapsed (Time %), memory usage percentage and amount (Mem %, Net(MB)). These are quite important metrics, moreover, we knew the huge overhead of import in Python, and we’ve seen it again.

There are a bunch of options for scalene, e.g. --profile-all flag profiles the program in every aspect, CPU, GPU, Memory usage included.

You can check out man scalene or scalene github page for more detailed information.

Pitfalls

PLEASE keep in mind that;

Premature optimization is the root of all evil.

Perfect is the enemy of good.

In other words, do not waste your time and energy with unnecessary optimizations which would not affect overall performance adequately. Otherwise, you’ll find yourself confused and lost, end up with a complicated and unreadable code. Just optimize inefficient code and avoid overdoing optimizations.

Conclusion#

You can improve your overall performance by profiling and optimizing your code in different aspects. You can profile and compare functions and libraries all you want. But writing an efficient algorithm is the best way of optimizing. That is to say, write better code.

Closing tips:

  • Use Python’s built-in functions as much as you can. They are quite fast, constantly getting better, and fit for almost every situation.
  • Don’t overdo your optimizations. Instead, work on your real priorities.
  • Write better algorithms. It’s the best way of optimization.
  • Good is enough. You can just get close to perfect, you can’t reach it. Don’t bother.

--

--

Bilal Özdemir
blutv
Writer for

I’m a software developer and a mathematician. I enjoy sharing what I learn.