Executive summary

I read a piece by Rodrigo Girão Serrão reviewing the new profiling module in Python 3.15, which tidies up two built-in profilers — a sampling profiler called Tachyon and the tracing profiler we used to know as cProfile — into a single namespace. The piece explains when each one is suitable to use, since they have quite different trade-offs when it comes to accuracy and overhead.

Problem solved

Before Python 3.15, the stdlib's profiling tooling was a bit messy: there was the old profile module, which was pure-Python and, according to the article, "of little practical use," and then there was cProfile, a separate module on its own. Through PEP 799, Python 3.15 unifies everything under the profiling module, with two submodules: profiling.sampling (Tachyon) and profiling.tracing (the successor to cProfile). The old profile module is officially deprecated in 3.15 and scheduled for removal in Python 3.17, so if there's still code importing it, that's already starting to become technical debt.

Key features and code examples

What I find interesting is that the article emphasizes that the two profilers aren't replacements for each other, but tools with different purposes:

  • profiling.sampling (Tachyon): a statistical profiler that takes snapshots of the call stack at high frequency, has near-zero overhead, and has advanced capabilities (attaching to an already-running process, profiling concurrent/async programs, GIL mode).
  • profiling.tracing (formerly cProfile): a deterministic profiler that instruments the code so its results are exact (call counts are guaranteed correct), but its overhead is moderate and it can only be used via CLI or programmatically — it can't attach to an already-running process.

The way you use them from the command line is also slightly different. The tracing profiler accepts the script name directly as an argument:

python -m profiling.tracing myscript.py

Tachyon, meanwhile, has a more complete CLI, so it needs the run subcommand:

python -m profiling.sampling run myscript.py

To save the results to a file instead of printing to the screen, just add the -o flag before the script name:

# Tracing profiler
python -m profiling.tracing -o stats.prof myscript.py

# Sampling profiler (Tachyon)
python -m profiling.sampling -o stats.prof myscript.py

The resulting file can be read again via the pstats module — here's an illustrative example from the article for reading those statistics:

import pstats

statistics = pstats.Stats("stats.prof")
statistics.print_stats()

Important note: the tracing profiler only produces output compatible with pstats, while Tachyon supports other output formats like flamegraph and heatmap.

Impact and upgrade notes

The article's default recommendation is quite clear: use Tachyon (profiling.sampling) as the first choice because its overhead is minimal and its capabilities are more modern, then move to the tracing profiler (profiling.tracing) only if you need (1) exact call counts, (2) programmatic use — since Tachyon only has a CLI and can't be invoked programmatically — or (3) to measure small performance differences (1–2%), which might not be caught by Tachyon's statistical nature. For micro-benchmarking at the level of small expressions, the article reminds readers to stick with timeit, not any profiler.

For anyone still relying on import profile or import cProfile directly, now's the time to start migrating to profiling.tracing before the profile module is completely removed in Python 3.17. The article also mentions advanced features it doesn't cover in detail — programmatic profiling with the tracing profiler, manipulating statistics via pstats, and Tachyon's features for attaching to a running process and profiling concurrent/async programs — so if you need those, the official documentation (docs.python.org/3/library/profiling.html) is the reference to go to.

---

First announced on Planet Python. Read the original source for full details.