Pyinstrument: A Low Overhead Python Profiler That Doesn’t Get in Your Way
If you’ve ever tried profiling a Python application, you know the pain. The standard profilers (like cProfile) give you mountains of raw data, but figuring out what’s actually slow usually requires staring at call trees for way too long. And the overhead? It can change the behavior of your code, making the profile misleading.
Enter Pyinstrument. It’s a statistical profiler that records call stacks at intervals, not every single function call. That means much lower overhead and results that actually reflect how your code performs in real life. It outputs something you can actually read without a PhD in Python internals.
What It Does
Pyinstrument is a Python profiler that samples the call stack at a regular interval (default is every millisecond). Instead of instrumenting every function call, it takes snapshots of what the program is doing. This gives you a profile that shows you which functions are taking the most wall-clock time, not just CPU time. So if your code is waiting on I/O, network, or sleeping, Pyinstrument shows you that too.
It also handles multi-threaded programs decently, and it can profile code running in different interpreters like PyPy or even inside a Jupyter notebook.
Why It’s Cool
The big thing is the output format. Pyinstrument gives you a tree where the most time-consuming paths are collapsed and sorted. The default console output looks like a nice, clean list of “here’s where the time goes” rather than an endless wall of numbers. You get:
_ ._ __/__ _ _ _ _ _/_ Recorded: 16:45:42
/_//_/// /_\ / //_// / //_'/ // Duration: 1.23s
/ _/ v4.2.0
1.23s <module> __main__.py:1
├─ 0.84s some_slow_function my_module.py:10
│ ├─ 0.50s inner_loop my_module.py:15
│ └─ 0.34s another_call my_module.py:20
└─ 0.39s setup utils.py:5
└─ 0.39s read_file utils.py:12
It also supports rendering to HTML (with interactive flame charts), JSON, and even speedscope format for use with other tools.
Another neat feature: it works with async code. Because it’s sampling based, it doesn’t get confused by await points. That’s a real hassle with most profilers.
How to Try It
Install it with pip:
pip install pyinstrument
Then profile any script:
pyinstrument my_script.py
Or use it as a context manager in your code:
from pyinstrument import Profiler
profiler = Profiler()
profiler.start()
# Your code here
profiler.stop()
print(profiler.output_text(unicode=True, color=True))
If you want a quick demo, just point it at any Python file you have lying around:
pyinstrument -r html your_script.py
That outputs an interactive HTML report you can open in a browser.
Final Thoughts
Pyinstrument is one of those tools you install once and wonder how you lived without it. It’s not trying to replace cProfile for deep dives into CPU performance. But for day-to-day work, where you just want to know “why is this thing slow?” it’s way more practical. The low overhead means you can even run it in production for a few seconds without breaking anything.
For anyone writing Python, especially if you deal with web servers, data pipelines, or anything with external I/O, give it a shot. It’s one of the few profiling tools that actually respects your time.
Brought to you by @githubprojects
Repository: https://github.com/joerick/pyinstrument