Last Updated on November 14, 2023
You can use the profilehooks to automatically benchmark and profile Python code.
This can be achieved by first installing the library and then adding function decorators to target functions. The target function can then be run normally and the benchmark and profile information will be reported.
In this tutorial, you will discover how to benchmark and profile Python code using the profilehooks open-source library.
Let’s get started.
What is profilehooks
The profilehooks project is a Python library for profiling and benchmarking.
It was developed by Marius Gedminas and is available as open source on GitHub.
Python decorators for profiling/tracing/timing a single function
— profilehooks, GitHub.
The library provides a number of function decorators that can be imported and added to target functions.
When the program is run, the function decorators will automatically benchmark and profile the target function and report the results.
This provides a convenient and easy way to benchmark and profile Python code.
Sometimes you want to profile just a single function in your Python program. The profilehooks module lets you do that very easily
— profilehooks: Decorators for profiling Python functions
Now that we know about the profilehooks project, let’s look at how we can use it.
Run loops using all CPUs, download your FREE book to learn how.
How to Use profilehooks
Using the profilehooks library for profiling and benchmarking involves three steps, they are:
- Install profilehooks.
- Add function decorators.
- Run the program and review the results.
Let’s take a closer look at each in turn.
1. Install profilehooks
The first step is to install the profilehooks library.
This can be achieved using your favorite Python package manager, such as pip.
For example:
1 |
pip install profilehooks |
2. Add Function Decorators
Once installed, we can add decorators to functions.
The profilehooks library offers three main decorators:
- profilehooks.profile: For profiling a target function.
- profilehooks.timecall: For benchmarking a target function.
- profilehooks.coverage: For line coverage of a target function.
for example:
1 2 3 |
@profile def myfunc(): ... |
3. Run And Review Results
The final step is to run the program with the decorators.
When the program is run the target function/s will be profiled or benchmarked and the results are reported to standard output.
And that’s it.
Now that we know how to use the profilehooks library, let’s look at some worked examples.
Example of Benchmarking with profilehooks
We can explore how to use the profilehooks library to benchmark a target function.
In computing, a benchmark is the act of running a computer program, a set of programs, or other operations, in order to assess the relative performance of an object, normally by running a number of standard tests and trials against it.
— Benchmark (computing), Wikipedia.
In this case, we will define a target function that creates a list of squared integers and takes an argument that defines the size of the list.
1 2 3 |
# square using power operator ** def create_list(size): return [i**2 for i in range(size)] |
We can then update the target function to add the profilehooks.timecall decorator.
This will automatically benchmark the target function.
1 2 3 4 |
# square using power operator ** @timecall def create_list(size): return [i**2 for i in range(size)] |
Finally, we can call the target function and specify the size of a list to create.
In this case, we will create a list of ten million squared integers.
1 2 3 |
... # run the target function data = create_list(10000000) |
Tying this together, the complete example is listed below.
1 2 3 4 5 6 7 8 9 10 11 |
# SuperFastPython.com # example of benchmarking a function with profilehooks from profilehooks import timecall # square using power operator ** @timecall def create_list(size): return [i**2 for i in range(size)] # run the target function data = create_list(10000000) |
Running the example creates the list of ten million squared integers.
The create_list() target function is automatically benchmarked and the execution time of the function is reported.
In this case, the function took about 0.630 seconds to execute.
This highlights how we can use the profilehooks to benchmark target functions.
1 2 |
create_list (.../program.py:6): 0.630 seconds |
Next, let’s look at how we might use the profilehooks library to profile a target function.
Free Python Benchmarking Course
Get FREE access to my 7-day email course on Python Benchmarking.
Discover benchmarking with the time.perf_counter() function, how to develop a benchmarking helper function and context manager and how to use the timeit API and command line.
Example of Profiling with profilehooks
We can explore how to use the profilehooks library to profile a target function.
Recall that profiling and benchmarking are related but quite different activities. Benchmarking is focused on measuring the overall execution time whereas profiting is about understanding the execution time of target code so that it can be improved.
In software engineering, profiling (“program profiling”, “software profiling”) is a form of dynamic program analysis that measures, for example, the space (memory) or time complexity of a program, the usage of particular instructions, or the frequency and duration of function calls. Most commonly, profiling information serves to aid program optimization, and more specifically, performance engineering.
— Profiling (computer programming), Wikipedia.
In this case, we will update the target function from the above example to use the profilehooks.profile function decorator
1 2 3 4 |
# square using power operator ** @profile def create_list(size): return [i**2 for i in range(size)] |
Tying this together, the complete example is listed below.
1 2 3 4 5 6 7 8 9 10 11 |
# SuperFastPython.com # example of profiling a function with profilehooks from profilehooks import profile # square using power operator ** @profile def create_list(size): return [i**2 for i in range(size)] # run the target function data = create_list(10000000) |
Running the example creates the list of ten million squared integers.
The create_list() target function is automatically profiled and the results are reported on standard output.
We can see that the profile output highlights the list comprehension as the slow part of the target function, as we might expect.
This highlights how we can use the profilehooks to profile target functions.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
*** PROFILER RESULTS *** create_list (.../program.py:6) function called 1 times 3 function calls in 0.878 seconds Ordered by: cumulative time, internal time, call count ncalls tottime percall cumtime percall filename:lineno(function) 1 0.000 0.000 0.878 0.878 program.py:6(create_list) 1 0.878 0.878 0.878 0.878 program.py:8(<listcomp>) 1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects} 0 0.000 0.000 profile:0(profiler) |
Next, let’s look at how we might use the profilehooks library to report line coverage of a target function.
Overwhelmed by the python concurrency APIs?
Find relief, download my FREE Python Concurrency Mind Maps
Example of Coverage with profilehooks
We can explore how to use the profilehooks library to line coverage a target function.
Recall that line coverage refers to whether or not each line in a target function is executed and/or the number of times it is executed. It is often helpful to measure when executing unit tests to confirm that all lines of code were exercised by the tests.
In software engineering, code coverage is a percentage measure of the degree to which the source code of a program is executed when a particular test suite is run.
— Code coverage, Wikipedia.
In this case, we will update the target function from the above example to use the profilehooks.coverage function decorator
1 2 3 4 |
# square using power operator ** @coverage def create_list(size): return [i**2 for i in range(size)] |
Tying this together, the complete example is listed below.
1 2 3 4 5 6 7 8 9 10 11 |
# SuperFastPython.com # example of coverage a function with profilehooks from profilehooks import coverage # square using power operator ** @coverage def create_list(size): return [i**2 for i in range(size)] # run the target function data = create_list(10000000) |
Running the example creates the list of ten million squared integers.
The coverage of the create_list() target function is automatically recorded and reported.
We can see that the single line of the target function was executed a little more than ten million times, as we might expect.
This highlights how we can use the profilehooks to report line coverage target functions.
1 2 3 4 5 6 7 8 9 |
*** COVERAGE RESULTS *** create_list (.../program.py:6) function called 1 times >>>>>> @coverage def create_list(size): 10000002: return [i**2 for i in range(size)] 1 lines were not executed. |
Further Reading
This section provides additional resources that you may find helpful.
Books
- Python Benchmarking, Jason Brownlee (my book!)
Also, the following Python books have chapters on benchmarking that may be helpful:
- Python Cookbook, 2013. (sections 9.1, 9.10, 9.22, 13.13, and 14.13)
- High Performance Python, 2020. (chapter 2)
Guides
- 4 Ways to Benchmark Python Code
- 5 Ways to Measure Execution Time in Python
- Python Benchmark Comparison Metrics
Benchmarking APIs
- time — Time access and conversions
- timeit — Measure execution time of small code snippets
- The Python Profilers
References
Takeaways
You now know how to benchmark and profile Python code using the profilehooks open-source library.
Did I make a mistake? See a typo?
I’m a simple humble human. Correct me, please!
Do you have any additional tips?
I’d love to hear about them!
Do you have any questions?
Ask your questions in the comments below and I will do my best to answer.
Photo by Francesco Lo Giudice on Unsplash
Do you have any questions?