Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,11 @@ repos:
- id: end-of-file-fixer
- id: trailing-whitespace
- repo: https://github.com/tox-dev/pyproject-fmt
rev: v2.11.1
rev: v2.16.2
hooks:
- id: pyproject-fmt
- repo: https://github.com/abravalheri/validate-pyproject
rev: v0.24.1
rev: v0.25
hooks:
- id: validate-pyproject
- repo: https://github.com/sphinx-contrib/sphinx-lint
Expand All @@ -37,14 +37,14 @@ repos:
- id: sphinx-lint
types: [rst]
- repo: https://github.com/pycqa/isort
rev: 7.0.0
rev: 8.0.1
hooks:
- id: isort
additional_dependencies: ["toml"]
entry: isort --profile=black
name: isort (python)
- repo: https://github.com/psf/black-pre-commit-mirror
rev: 25.12.0
rev: 26.1.0
hooks:
- id: black
- repo: https://github.com/tonybaloney/perflint
Expand Down
1 change: 1 addition & 0 deletions docs/data-processing/apis/grpc/accounts_pb2.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
# Generated by the protocol buffer compiler. DO NOT EDIT!
# source: accounts.proto
"""Generated protocol buffer code."""

from google.protobuf import descriptor as _descriptor
from google.protobuf import message as _message
from google.protobuf import reflection as _reflection
Expand Down
1 change: 1 addition & 0 deletions docs/data-processing/apis/grpc/accounts_pb2_grpc.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

# Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT!
"""Client and server classes corresponding to protobuf-defined services."""

import accounts_pb2 as accounts__pb2
import grpc

Expand Down
19 changes: 19 additions & 0 deletions docs/data-processing/serialisation-formats/json/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,25 @@ JSON tools
.. image:: https://raster.shields.io/github/license/stefankoegl/python-json-patch
:alt: Licence

`jsonata-python <https://github.com/rayokota/jsonata-python>`_
`JSONata <https://docs.jsonata.org/overview.html>`_ is a lightweight query
and transformation language for JSON data, inspired by XPath.

.. image:: https://raster.shields.io/github/stars/rayokota/jsonata-python
:alt: Stars
:target: https://github.com/rayokota/jsonata-python

.. image:: https://raster.shields.io/github/contributors/rayokota/jsonata-python
:alt: Contributors
:target: https://github.com/rayokota/jsonata-python/graphs/contributors

.. image:: https://raster.shields.io/github/commit-activity/y/rayokota/jsonata-python
:alt: Commit activity
:target: https://github.com/rayokota/jsonata-python/graphs/commit-activity

.. image:: https://raster.shields.io/github/license/rayokota/jsonata-python
:alt: Lizenz

.. _`standard`: https://www.json.org/json-en.html
.. _`JSON_Checker`: http://www.json.org/JSON_checker/
.. _`JSON Schema Proposal`: https://json-schema.org
Expand Down
32 changes: 27 additions & 5 deletions docs/performance/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ it is usually counterproductive to worry about the efficiency of the code.
<http://www.literateprogramming.com/>`_, in Computer Programming as an
Art (1974)

.. seealso::
* `Speed up your data science and scientific computing code
<https://pythonspeed.com/datascience/>`_

k-Means example
---------------

Expand Down Expand Up @@ -62,18 +66,28 @@ Performance measurements
------------------------

Once you have worked with your code, it can be useful to examine its efficiency
more closely. `cProfile
<https://docs.python.org/3.14/library/profile.html#module-cProfile>`_,
:doc:`ipython-profiler` or :doc:`scalene` can be used for this.
more closely. :doc:`cProfile <tracing>`, :doc:`ipython-profiler`,
:doc:`scalene`, :doc:`tprof` or :doc:`memray` can be used for this. So far, I
usually carry out the following steps:

:doc:`cProfile <tracing>`, :doc:`ipython-profiler`, :doc:`scalene`
or :doc:`tprof` can be used for this. So far, I usually carry out the following
steps:

#. I profile the entire programme with :doc:`cProfile <tracing>` or `py-spy
<https://github.com/benfred/py-spy>`_ to find slow functions.
#. Then I optimise a slow function.
#. Finally, I create a new profile and filter out the result of my optimised
version so that I can compare the results.

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

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

Expand All @@ -91,8 +105,11 @@ more closely. `cProfile
:titlesonly:
:maxdepth: 0

tracing
ipython-profiler.ipynb
scalene.ipynb
tprof
memray
tachyon

Search for existing implementations
Expand Down Expand Up @@ -282,6 +299,11 @@ scientific Python and NumPy code into fast machine code, for example:
However, Numba requires `LLVM <https://en.wikipedia.org/wiki/LLVM>`_ and some
Python constructs are not supported.

.. seealso::
* `Speeding up NumPy with parallelism
<https://pythonspeed.com/articles/numpy-parallelism/> by Itamar
Turner-Trauring`_

Task planner
------------

Expand Down
Binary file added docs/performance/memray-flamegraph.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
93 changes: 93 additions & 0 deletions docs/performance/memray.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
.. SPDX-FileCopyrightText: 2026 Veit Schiele
..
.. SPDX-License-Identifier: BSD-3-Clause

Memray
======

Memory usage is difficult to control in Python projects because the language
does not explicitly indicate where memory is allocated, module imports can
significantly increase consumption, and it is all too easy to create a data
structure that accidentally grows indefinitely. Data science projects are
particularly prone to high memory consumption, as they usually import many large
dependencies such as :doc:`/workspace/numpy/index`, even if these are only used
in a few places.

`Memray <https://bloomberg.github.io/memray/>`_ helps you understand your
programme’s memory usage by tracking where memory is allocated and freed during
programme execution. This data can then be displayed in various ways, including
`flame graphs <https://www.brendangregg.com/flamegraphs.html>`_, which summarise
`stack traces <https://en.wikipedia.org/wiki/Stack_trace>`_ in a diagram, with
the bar width representing the size of the memory allocation.

With ``memray run``, any Python command can be profiled. For most projects, it
is recommended to first use check to profile the function that loads your
project. This checks the minimum effort required to start your application, for
example:

.. code-block:: console

$ uv run memray run src/items/__init__.py check
Writing profile results into src/items/memray-__init__.py.72633.bin
[memray] Successfully generated profile results.

You can now generate reports from the stored allocation records.
Some example commands to generate reports:

/Users/veit/items/.venv/bin/python3 -m memray flamegraph src/items/memray-__init__.py.72633.bin

The command outputs the message ``Successfully generated profile results.`` and
creates a :samp:`{PROCESS-ID}.bin` file. We can then create the flame graph
with:

.. code-block:: console

$ uv run python -m memray flamegraph src/items/memray-__init__.py.72633.bin
Wrote src/items/memray-flamegraph-__init__.py.72633.html

.. tip::
In many consoles, you can combine the two commands with ``&&``:

.. code-block:: console

$ uv run memray run src/items/__init__.py check && uv run python -m memray flamegraph src/items/memray-__init__.py.72633.bin

The result is the following HTML file:

.. figure:: memray-flamegraph.png
:alt: memray flamegraph report

memray flamegraph report

The header area of the page contains several controls, including

*Memory Graph*
Display of the memory space of a process in the working memory (`resident
set size <https://en.wikipedia.org/wiki/Resident_set_size>`_) and the
dynamic memory (heap memory) over time
die Zeit
*Stats*
Memory statistics, in this case

.. code-block:: text

Command line: /Users/veit/items/.venv/bin/memray run src/items/api.py check
Start time: Sun Feb 08 2026 12:12:27 GMT+0100 (Central European Standard Time)
End time: Sun Feb 08 2026 12:12:27 GMT+0100 (Central European Standard Time)
Duration: 0:00:00.068000
Total number of allocations: 11142
Total number of frames seen: 0
Peak memory usage: 4.6 MB
Python allocator: pymalloc

Below that is the flame graph as an icicle chart showing memory allocations over
time, with the last call at the bottom. The graph shows the line of code
executed at a given point in time, with the width proportional to the amount of
memory allocated; if you move your mouse over it, you will see further details
such as file name, line number, allocated memory and number of allocations.

.. tip::
With :ref:`python-basics:pytest_memray`, there is also a plugin for
:doc:`python-basics:test/pytest/index` that allows you to check whether the
upper limits you have set for memory consumption and memory leaks are being
adhered to.
67 changes: 67 additions & 0 deletions docs/performance/tprof.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
.. SPDX-FileCopyrightText: 2026 Veit Schiele
..
.. SPDX-License-Identifier: BSD-3-Clause

``tprof``
=========

`tprof <https://github.com/adamchainz/tprof>`_ measures from Python 3.12 onwards
the time spent executing a module in specific functions. Unlike other profilers,
it only tracks the specified functions with :mod:`sys.monitoring`, eliminating
the need for filtering.

``tprof`` supports use as a command line programme and with a Python interface:

:samp:`uv run tprof -t {MODULE}:{FUNCTION} (-m {MODULE} | {PATH/TO/SCRIPT})`
Suppose you have determined that creating :class:`pathlib.Path` objects in
the :mod:`main` module is slowing down your code. Here’s how you can measure
this with ``tprof``:

.. code-block:: console

$ uv run tprof -t pathlib:Path.open -m main
🎯 tprof results:
function calls total mean ± σ min … max
pathlib:Path.open() 1 93μs 93μs 93μs … 93μs

With the ``-x`` option, you can also compare two functions with each other:

.. code-block:: console

$ uv run tprof -x -t old -m main -t new -m main
🎯 tprof results:
function calls total mean ± σ min … max delta
main:old() 1 41μs 41μs 41μs … 41μs -
main:new() 1 20μs 20μs 20μs … 20μs -50.67%

``tprof(*targets, label: str | None = None, compare: bool = False)``
uses this code as a :doc:`context manager <python-basics:control-flow/with>`
in your code to perform profiling in a specific block. The report is
generated each time the block is run through.

``*targets``
are callable elements for profiling or references to elements that are
resolved with :func:`pkgutil.resolve_name`.
``label``
is an optional string that can be added to the report as a header.
``compare``
set to ``True`` activates comparison mode.

Example:

.. code-block:: Python

from pathlib import Path

from tprof import tprof

with tprof(Path.open):
p = Path("docs", "save-data", "myfile.txt")
f = p.open()

.. code-block:: console

$ uv run python main.py
🎯 tprof results:
function calls total mean ± σ min … max
pathlib:Path.open() 1 82μs 82μs 82μs … 82μs
45 changes: 45 additions & 0 deletions docs/performance/tracing.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
.. SPDX-FileCopyrightText: 2026 Veit Schiele
..
.. SPDX-License-Identifier: BSD-3-Clause

cProfile/profiling.tracing
==========================

Usually, a profile is created in the command line with `cProfile
<https://docs.python.org/3.14/library/profile.html#module-cProfile>`_ or, from
Python 3.15 onwards, with :mod:`profiling.tracing`, which then displays its
profile statistics. However, this can quickly become very tedious, especially
when reading extensive profiles or sorting the data. A more flexible approach is
to save the profile data in a file instead, which can then be read with the
:mod:`pstats` module:

#. :samp:`uv run python -m cProfile -o {PROFILE} ({SCRIPT} | {-m {MODULE})`
runs `cProfile
<https://docs.python.org/3.14/library/profile.html#module-cProfile>`_ to
profile your script or module and saves the results in a file specified by
the ``-o`` option.

#. :samp:`uv run python -m (cProfile | profiling.tracing) -o profile ({SCRIPT} |
-m {MODULE}) <<< $'sort cumtime\nstats 100' | less` passes the following two
commands to the :mod:`pstats` module using the ``$`` syntax.

``sort cumtime``
sorts the output by cumulative time, starting with the largest.

To sort by other metrics, simply replace ``cumtime`` with a value from
:meth:`pstats.Stats.sort_stats`.

``stats 100``
displays the first 100 lines of the profile.

The output is passed to ``less`` so you can view the results. Press :kbd:`q`
to exit when you are finished.

#. Before and after optimisation can be easily compared, for example with:

.. code-block:: console

$ uv run python -m cProfile -o before.profile main.py
$ git switch -c main_optimisation
...
$ uv run python -m cProfile -o after.profile main.py
Loading
Loading