Skip to content

Commit 5fcd769

Browse files
committed
📝 Update performance measurements section
* Add cProfile/profiling.tracing * Add tprof
1 parent eefba94 commit 5fcd769

File tree

3 files changed

+125
-5
lines changed

3 files changed

+125
-5
lines changed

docs/performance/index.rst

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -62,18 +62,24 @@ Performance measurements
6262
------------------------
6363

6464
Once you have worked with your code, it can be useful to examine its efficiency
65-
more closely. `cProfile
66-
<https://docs.python.org/3.14/library/profile.html#module-cProfile>`_,
67-
:doc:`ipython-profiler` or :doc:`scalene` can be used for this.
65+
more closely. :doc:`cProfile <tracing>`, :doc:`ipython-profiler`, :doc:`scalene`
66+
or :doc:`tprof` can be used for this. So far, I usually carry out the following
67+
steps:
68+
69+
#. I profile the entire programme with :doc:`cProfile <tracing>` or `py-spy
70+
<https://github.com/benfred/py-spy>`_ to find slow functions.
71+
#. Then I optimise a slow function.
72+
#. Finally, I create a new profile and filter out the result of my optimised
73+
version so that I can compare the results.
6874

6975
.. versionadded:: Python3.15
7076
:pep:`799` will provide a special profiling module that organises the
7177
profiling tools integrated in Python under a uniform namespace. This module
7278
contains:
7379

7480
:mod:`profiling.tracing`
75-
deterministic function call tracing, which has been moved from `cProfile
76-
<https://docs.python.org/3.14/library/profile.html#module-cProfile>`_.
81+
deterministic function call tracing, which has been moved from
82+
:doc:`cProfile <tracing>`.
7783
:mod:`profiling.sampling`
7884
the new statistical sampling profiler :doc:`tachyon`.
7985

@@ -91,8 +97,10 @@ more closely. `cProfile
9197
:titlesonly:
9298
:maxdepth: 0
9399

100+
tracing
94101
ipython-profiler.ipynb
95102
scalene.ipynb
103+
tprof
96104
tachyon
97105

98106
Search for existing implementations

docs/performance/tprof.rst

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
.. SPDX-FileCopyrightText: 2026 Veit Schiele
2+
..
3+
.. SPDX-License-Identifier: BSD-3-Clause
4+
5+
``tprof``
6+
=========
7+
8+
`tprof <https://github.com/adamchainz/tprof>`_ measures from Python 3.12 onwards
9+
the time spent executing a module in specific functions. Unlike other profilers,
10+
it only tracks the specified functions with :mod:`sys.monitoring`, eliminating
11+
the need for filtering.
12+
13+
``tprof`` supports use as a command line programme and with a Python interface:
14+
15+
:samp:`uv run tprof -t {MODULE}:{FUNCTION} (-m {MODULE} | {PATH/TO/SCRIPT})`
16+
Suppose you have determined that creating :class:`pathlib.Path` objects in
17+
the :mod:`main` module is slowing down your code. Here’s how you can measure
18+
this with ``tprof``:
19+
20+
.. code-block:: console
21+
22+
$ uv run tprof -t pathlib:Path.open -m main
23+
🎯 tprof results:
24+
function calls total mean ± σ min … max
25+
pathlib:Path.open() 1 93μs 93μs 93μs … 93μs
26+
27+
With the ``-x`` option, you can also compare two functions with each other:
28+
29+
.. code-block:: console
30+
31+
$ uv run tprof -x -t old -m main -t new -m main
32+
🎯 tprof results:
33+
function calls total mean ± σ min … max delta
34+
main:old() 1 41μs 41μs 41μs … 41μs -
35+
main:new() 1 20μs 20μs 20μs … 20μs -50.67%
36+
37+
``tprof(*targets, label: str | None = None, compare: bool = False)``
38+
uses this code as a :doc:`context manager <python-basics:control-flow/with>`
39+
in your code to perform profiling in a specific block. The report is
40+
generated each time the block is run through.
41+
42+
``*targets``
43+
are callable elements for profiling or references to elements that are
44+
resolved with :func:`pkgutil.resolve_name`.
45+
``label``
46+
is an optional string that can be added to the report as a header.
47+
``compare``
48+
set to ``True`` activates comparison mode.
49+
50+
Example:
51+
52+
.. code-block:: Python
53+
54+
from pathlib import Path
55+
56+
from tprof import tprof
57+
58+
with tprof(Path.open):
59+
p = Path("docs", "save-data", "myfile.txt")
60+
f = p.open()
61+
62+
.. code-block:: console
63+
64+
$ uv run python main.py
65+
🎯 tprof results:
66+
function calls total mean ± σ min … max
67+
pathlib:Path.open() 1 82μs 82μs 82μs … 82μs

docs/performance/tracing.rst

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
.. SPDX-FileCopyrightText: 2026 Veit Schiele
2+
..
3+
.. SPDX-License-Identifier: BSD-3-Clause
4+
5+
cProfile/profiling.tracing
6+
==========================
7+
8+
Usually, a profile is created in the command line with `cProfile
9+
<https://docs.python.org/3.14/library/profile.html#module-cProfile>`_ or, from
10+
Python 3.15 onwards, with :mod:`profiling.tracing`, which then displays its
11+
profile statistics. However, this can quickly become very tedious, especially
12+
when reading extensive profiles or sorting the data. A more flexible approach is
13+
to save the profile data in a file instead, which can then be read with the
14+
:mod:`pstats` module:
15+
16+
#. :samp:`uv run python -m cProfile -o {PROFILE} ({SCRIPT} | {-m {MODULE})`
17+
runs `cProfile
18+
<https://docs.python.org/3.14/library/profile.html#module-cProfile>`_ to
19+
profile your script or module and saves the results in a file specified by
20+
the ``-o`` option.
21+
22+
#. :samp:`uv run python -m (cProfile | profiling.tracing) -o profile ({SCRIPT} |
23+
-m {MODULE}) <<< $'sort cumtime\nstats 100' | less` passes the following two
24+
commands to the :mod:`pstats` module using the ``$`` syntax.
25+
26+
``sort cumtime``
27+
sorts the output by cumulative time, starting with the largest.
28+
29+
To sort by other metrics, simply replace ``cumtime`` with a value from
30+
:meth:`pstats.Stats.sort_stats`.
31+
32+
``stats 100``
33+
displays the first 100 lines of the profile.
34+
35+
The output is passed to ``less`` so you can view the results. Press :kbd:`q`
36+
to exit when you are finished.
37+
38+
#. Before and after optimisation can be easily compared, for example with:
39+
40+
.. code-block:: console
41+
42+
$ uv run python -m cProfile -o before.profile main.py
43+
$ git switch -c main_optimisation
44+
...
45+
$ uv run python -m cProfile -o after.profile main.py

0 commit comments

Comments
 (0)