Measuring the performance of Python chunks in different environments

cr0hn
3 min readAug 8, 2022

There are many libraries for measuring the performance of Python code. These tools are called profiling tools.

Even the Python core library has its own, it’s a bit cumbersome to use it, and, usually, they were not created for living with our production code.

Some examples are:

  • timeit
  • profile
  • cprofile

Today, I’ll present you a tiny library for easy measure-specific chunks of Python code in a straightforward way.

Let’s just say we have this code:

# file: bottleneck_functions

import time

def bottleneck_1():
r = 0

for x in range(4000)
r += ((x*x) * x) / 1000

def bottleneck_2():
for x in range(5000):
time.sleep(0.02)

def bottleneck_3():
for x in range(10000):
time.sleep(0.001)

def main():
function_1()

function_2()

function_3()

After executing the above code, we could not be sure what of them was the bottleneck of our program.

As I told you, it is possible to use timeit, for example:

import timeit

if __name__ == '__main__':
print(timeit.timeit("bottleneck_1()", setup="from bottleneck_functions import bottleneck_1"))
print(timeit.timeit("bottleneck_2()", setup="from bottleneck_functions import bottleneck_2"))
print(timeit.timeit("bottleneck_3()", setup="from bottleneck_functions import bottleneck_3"))

But it’s not intuitive and hard to use in real scenarios.

Imagine, for example, a simple web application that uses the above 3 functions and:

  1. We want to measure the time taken for each of them.
  2. We don’t want to change the source code depending on the environment where our app is running,
  3. We want that only work in the development and staging environment.

Did you get it? Right. Let’s write our sample web application:

# file: web_app.py

from flask import Flask

from bottleneck_functions import *

app = Flask(__name__)

@app.route("/", methods=["GET"])
def home():
bottleneck_1()

bottleneck_2()

bottleneck_3()

return "Ok!"

app.run()

Currently, we cannot measure the time taken for each function and detect the possible bottlenecks.

So, we need python-performance-tools library:

> pip install python-performance-tools# file: web_app_profiling.py

from flask import Flask

from performance_tools import *
from bottleneck_functions import *

app = Flask(__name__)

@app.route("/", methods=["GET"])
def home():
with catch_time("bottleneck function 1"):
bottleneck_1()

with catch_time("bottleneck function 2"):
bottleneck_2()

with catch_time("bottleneck function 3"):
bottleneck_3()

return "Ok!"

app.run()

After running the web app and visiting the url:http://127.0.0.1:5000, you can see something like:

> python web_app_profiling.py
[.. OMITTED ... ]
Time: 0.035312797 :: bottleneck function 1
Time: 5.018828881 :: bottleneck function 2
Time: 12.981881883 :: bottleneck function 3

Nice! Now we know how long it takes to execute each function!

But… as I told you, we only want to execute the profiling on development and staging steps. Ok, let’s do it:

The catch_time has a second optional argument. This argument is an activation function. This is: a function that should return a boolean and only will be activated if it returns a True.

# file: web_app_profiling.py

from flask import Flask

from performance_tools import *
from bottleneck_functions import *

app = Flask(__name__)

environment = os.environ.get("ENVIRONMENT", None)

def activate_function():
if environment.lower() in ("development", "stating"):
return True
else:
return False


@app.route("/", methods=["GET"])
def home():
with catch_time("bottleneck function 1", activate_function):
bottleneck_1()

with catch_time("bottleneck function 2", activate_function):
bottleneck_2()

with catch_time("bottleneck function 3", activate_function):
bottleneck_3()

return "Ok!"

app.run()

Check then if it works.

Enabling profiling

> export ENVIRONMENT=development
> python web_app_profiling.py
[.. OMITTED ... ]
Time: 0.035312797 :: bottleneck function 1
Time: 5.018828881 :: bottleneck function 2
Time: 12.981881883 :: bottleneck function 3

Disabling profiling

> export ENVIRONMENT=production
> python web_app_profiling.py
[.. OMITTED ... ]
[.. NO PROFILING INFORMATION ... ]

References

  1. Python core profiling library: https://docs.python.org/3/library/profile.html
  2. Python timeit: https://docs.python.org/3/library/timeit.html
  3. Python Performance tools: https://github.com/cr0hn/python-performance-tools

--

--

cr0hn

REST API Cybersecurity and Hacking & Python Architect. +100 GitHub projects. Speaker