Pyinstrument is a Python profiler. A profiler is a tool to help you optimize your code - make it faster. To get the biggest speed increase you should focus on the slowest part of your program. Pyinstrument helps you find it!
☕️ Not sure where to start? Check out this video tutorial from calmcode.io!
pip install pyinstrument
Pyinstrument supports Python 3.8+.
To run Pyinstrument from a git checkout, there's a build step. Take a look at Contributing for more info.
To learn how to use pyinstrument, or to check the reference, head to the documentation.
pyinstrument script.py where script.py contains a class
serialized with pickle, you might encounter errors because the
serialisation machinery doesn't know where __main__ is. See this issue
for workarounds4 January 2026
--target-description (#408)12 August 2025
10 August 2025
2 July 2025
24 May 2025
23 January 2025
11 October 2024
Loads of improvements to the HTML renderer!
Timeline mode - see and zoom into an interactive linear timeline!
- HTML mode now has interactive options, rather than needing to set the upfront.
- Streamlined the design of the HTML page header.
- HTML Call stack view supports arrow key navigation.
- The way ‘library’ code is detected has been changed. Previously, if the string ‘/lib/’ occurred in the file path, that was considered library code (and collapsed by default). Now, pyinstrument captures the paths of the Python install and any active virtualenv/conda env at profile time. Files that are stored there are considered library. That should give fewer false positives.
- Calls to profiler.start() can now pass a target_description parameter, which is displayed in the profile readout.
Check my blog post for more info on the new features.
6 September 2024
glom on Python 3.12 or later, which mutate the locals() dict. (#336)UnicodeDecodeError on some platforms (#330)5 August 2024
2 August 2024
1 August 2024
with block, or a function/method decorator. This will profile the code and print a short readout into the terminal. (#327)flat argument to the console output, to present a flat list of functions (#294)26 January 2024
show_all option to Profiler.output_html8 November 2023
%pyinstrument (#278)12 October 2023
-c, which allows profiling code directly from the command line, like python -c. (#271)Profiler.write_html, for writing HTML output to a file directly. (#266)7 September 2023
1 September 2023
22 July 2023
[X frames hidden] in the output when frames were deleted due to __tracebackhide__ (#255)None in the console output (#254)5 June 2023
-p flat on the command line. This mode shows the heaviest frame as measured by self-time, which can be useful in some codebases. (#240)pstats files. This is the file format used by cprofile in the stdlib. It's less detailed than pyinstrument profiles, but it's compatible with more tools. (#236)--show-all option - pyinstrument will no longer remove Python-internal frames when this option is supplied. (#239)5 November 2022
__traceback_hide__ local variable will now be removed from the output (#217)--async_mode=enabled flag. (#212)21 August 2022
--interval (seconds, default 0.001) to change the interval that
pyinstrument samples a program. This is useful for long-running programs,
where increasing the interval reduces the memory overhead.Adds a command-line option -p --render-option that allows arbitrary
setting of render options. This lets you set options like
filter_threshold from the command line, by doing something like
pyinstrument -p processor_options.filter_threshold=0.
Here's the help output for the option:
-p RENDER_OPTION, --render-option=RENDER_OPTION
options to pass to the renderer, in the format
'flag_name' or 'option_name=option_value'. For
example, to set the option 'time', pass '-p
time=percent_of_total'. To pass multiple options, use
the -p option multiple times. You can set processor
options using dot-syntax, like '-p
processor_options.filter_threshold=0'. option_value is
parsed as a JSON value or a string.
- Adds the ability to view times in the console output as percentages,
rather than absolute times. Use the ConsoleRenderer option
time='percent_of_total', or on the command line, use -p, like
pyinstrument -p time=percent_of_total.
- Adds command line options for loading and saving pyinstrument sessions.
You can save the raw data for a pyinstrument session with -r session,
like pyinstrument -r session -o session.pyisession myscript.py. Loading
is via --load, e.g. pyinstrument --load session.pyisession.
- Command line output format is inferred from the -o output file
extension. So if you do pyinstrument -o profile.html myscript.py, you
don't need to supply -r html, pyinstrument will automatically use the
HTML renderer. Or if you do
pyinstrument -o profile.pyisession myscript.py, it will save a raw
session object.
- Adds usage examples for FastAPI and pytest to the documentation.
- Fixes a bug causing NotImplementedError when using async_mode=strict.
- Adds support for Python 3.11
%load_ext pyinstrument at the top of your notebook, and then
%%pyinstrument in the cell you want to profile.pyinstrument -r speedscope, and upload to the
speedscope web app.PYINSTRUMENT_PROFILE_DIR_RENDERER option.Async support! Pyinstrument now detects when an async task hits an await, and tracks time spent outside of the async context under this await.
So, for example, here's a simple script with an async task that does a sleep:
```python import asyncio from pyinstrument import Profiler
async def main(): p = Profiler(async_mode='disabled')
with p:
print('Hello ...')
await asyncio.sleep(1)
print('... World!')
p.print()
asyncio.run(main()) ```
Before Pyinstrument 4.0.0, we'd see only time spent in the run loop, like this:
``` _ . __/__ _ _ _ _ /_
$ claude mcp add pyinstrument \
-- python -m otcore.mcp_server <graph>