Profiling and Performance Conversion Scripts
Performance profiling is an essential aspect of optimizing and understanding the behavior of your Python code. Profiling helps identify bottlenecks and performance-critical sections in your application, allowing you to enhance its efficiency. This story explores a detailed procedure for converting .prof
files generated by Python's cProfile module into .callgrind.
The source code you can found here. It is available as part of my AlexBerUtils s project.
You can install AlexBerUtils from PyPi:
python -m pip install -U alex-ber-utils
See here for more details explanation on how to install.
High-Resolution Timer Profiling in Python
Before diving into the conversion process, let’s set up a cProfile profiler in Python using a high-resolution timer based on time.perf_counter_ns
. This timer offers greater precision in nanoseconds, which is particularly useful for performance-sensitive applications.
import cProfile
import time
import uvicorn
...
def app_start():
uvicorn.run("main:app", host='0.0.0.0', port=80)
# Define a more precise timer
def precise_timer():
return time.perf_counter_ns()
def profiler_start():
# Create a cProfile.Profile object with the precise timer function
profiler = cProfile.Profile(timer=precise_timer)
profiler.enable()
app_start()
profiler.disable()
profiler.dump_stats('my_program.prof')
# if __name__ == "__main__":
# profiler_start()
if __name__ == "__main__":
uvicorn.run("main:app", host='0.0.0.0', port=80)
# Usage note:
# The profiler is now configured to use the precise_timer function,
# which provides higher resolution timing measurements in nanoseconds.
main.py
With the profiler configured, you can use it to profile your Python code, and it will generate a .prof
file.
Converting .prof
Files to .callgrind
Format
You can Git checkout the source of AlexBerUtils
and run prof_to_callgrind.sh
. As long as you have Docker installed and internet access to https://hub.docker.com/
, it will work seamlessly.
Shell Script: prof_to_callgrind.sh
The prof_to_callgrind.sh
script automates the process of converting .prof
files to .callgrind
format using Docker. This script offers flexibility by allowing users to specify input and output files, project directories, and application directories. You can use it from the source or after installing via pip
. You will need to find it in your site-packages
directory. Optionally, you can add it to your /bin
, PATH
, or any other mechanism that will recognize it.
Python Script: prof_to_callgrind.py
The prof_to_callgrind.py
script handles the actual conversion of profile statistics from a .prof
file to a .callgrind
file. This script reads the profile data and writes it in a format compatible with callgrind
, which is used by tools like KCachegrind
for detailed profiling analysis. This script offers flexibility by allowing users to specify input and output files, project directories, and application directories.
Converting .prof
Files to .callgrind
Format
- From Source or after pip Installation
You can use the script either from the source repository or after installing it withpip
. Locate it in your site-packages directory. Optionally, you can add it to your/bin
,PATH
, or any other mechanism that will recognize it. - Using in Your Own Script
You can write your own script to callprof_to_callgrind.py
:
from alexber.utils.prof_to_callgrind import main
# your_script.py
if __name__ == "__main__":
main()
Running this script will eliminate the hassle of searching where the files are installed.
3. Integrating into Your Codebase
You can integrate prof_to_callgrind
directly into your code:
from alexber.utils.prof_to_callgrind import convert_prof_to_callgrind
def main():
# Your application logic here
input_file = 'input.prof'
output_file = 'output.callgrind'
convert_prof_to_callgrind(input_file, output_file)
if __name__ == "__main__":
main()
cProfile: Profiling Tool for Python
Python’s cProfile
module is a built-in tool for profiling Python applications. It provides a way to measure where time is spent in your code, which functions are called, and how often they are called.
Key Features of cProfile:
- Function Call Statistics: cProfile provides detailed statistics about function calls, including how many times each function is called.
- Execution Time: It measures the time spent in each function, helping identify performance bottlenecks.
- Integration with pstats: The output of cProfile can be saved and analyzed using Python’s
pstats
module, which supports various formatting options.
KCachegrind: Visualizing Profiling Data
KCachegrind is a powerful tool for visualizing profiling data, particularly from .callgrind
files. It provides a graphical interface that makes it easier to analyze performance data and understand detailed call paths and time distribution.
Features of KCachegrind:
- Graphical Representation: It offers a visual representation of function calls and their relationships.
- Call Graphs: You can explore call graphs that show the sequence of function calls and their execution times.
- Annotations: Detailed annotations help identify which lines of code are consuming the most time.
Steps to Use KCachegrind:
Install KCachegrind: Depending on your operating system, you can install KCachegrind via package managers like apt
, yum
, or through direct downloads.
Linux:
sudo apt-get update
sudo apt-get install kcachegrind
Mac
On macOS, you can install KCachegrind using a package manager Homebrew.
- Install Homebrew if you haven’t already. Open a terminal and run:
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
2. Install KCachegrind:
brew install kcachegrind
Windows
A. Using Windows Subsystem for Linux (WSL2)
- Open the installed Linux distribution (e.g., Ubuntu).
- Update the package list and install KCachegrind:
sudo apt-get update
sudo apt-get install kcachegrind
B. Using Homebrew on Windows with WSL2
- Install Homebrew if you haven’t already. Open a terminal and run:
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
2. Add Homebrew to your shell profile. Follow the instructions provided at the end of the installation script or add the following to your ~/.profile
or ~/.bashrc
:
echo 'eval "$(/home/linuxbrew/.linuxbrew/bin/brew shellenv)"' >> ~/.profile
eval "$(/home/linuxbrew/.linuxbrew/bin/brew shellenv)"
3. Once Homebrew is installed and set up, you can install KCachegrind:
brew install kcachegrind
Verification
After installation, you can verify that KCachegrind is installed by running:
kcachegrind
This should open the KCachegrind application.
Open .callgrind
Files:
Launch KCachegrind
and open the .callgrind
file generated by the conversion script. This will display an interactive interface where you can explore the profiling data.
This comprehensive approach facilitates efficient profiling and performance analysis of Python applications, leveraging the precision of high-resolution timers and the visualization capabilities of callgrind-compatible tools like KCachegrind
.
P.S. Microbenchmarking
import time
start = time.perf_counter_ns()
operation_to_benchmarknig()
delta = (time.perf_counter_ns() - start) * 10 ** (-9)
print(f"operation_to_benchmarknig took {delta:.4f} seconds")