profiling.sampling: Statistical Profiler in the Standard Library
profiling.sampling is the statistical sampling profiler that ships with Python 3.15, developed under the codename Tachyon. It samples the call stacks of a Python process on a timer rather than instrumenting every call, and it can attach to a process that is already running by PID. The CLI is invoked as python -m profiling.sampling, with run and attach as the two profiling entry points and replay for converting saved binary profiles to other output formats.
The new module sits inside the broader PEP 799 reorganization of the standard-library profilers. cProfile moves to profiling.tracing (with cProfile kept as a backward-compatible alias), the legacy pure-Python profile module is deprecated in 3.15 and scheduled for removal in 3.17, and the new sampling profiler lives at profiling.sampling.
Availability
Python 3.15 is in alpha as of May 2026; the final release is scheduled for October 2026. The CLI surface documented here matches 3.15.0a8, and flags may still change before the stable release.
uv installs alpha and beta Pythons with the same command it uses for stable releases:
uv python install 3.15 # pulls the latest published 3.15 (currently an alpha)
uv python install 3.15.0a8 # pins a specific pre-releaseuv python install 3.15 resolves to the latest version matching that minor, so it picks up newer alphas, betas, and the final release as they ship. To pin a project virtualenv to a specific pre-release, use uv venv --python 3.15.0a8. The Version Compatibility section explains why the exact pin matters when attaching the profiler to a running process.
When to use profiling.sampling
Reach for profiling.sampling when a Python process is running in production or in a long-running development session and a stack profile is needed without restarting it. The profiler attaches to a PID, runs out of process, and produces flame graphs, pstats output, or live terminal stats. It supersedes much of what required a third-party install before 3.15 (see py-spy and pyinstrument), although the third-party tools still cover Python versions before 3.15 and have more mature flame-graph ecosystems.
For exact call counts or per-line attribution, use the deterministic side of the package: profiling.tracing (the relocated cProfile) for call counts and line_profiler for line-level attribution. The trade-offs between the two designs are covered in Sampling vs deterministic profilers.
Subcommands
The CLI is invoked as python -m profiling.sampling <subcommand>:
| Subcommand | Purpose |
|---|---|
run |
Launch a Python script or module and profile it from startup |
attach |
Attach to a running process by PID and profile until duration elapses |
replay |
Convert a previously recorded binary profile to another output format |
Sampling Options
The default sampling rate is 1 kHz. The documented maximum is 1 MHz.
-r,--sampling-rateaccepts a number or a unit suffix (10000,10khz,10k)-d,--durationruns for a fixed number of seconds; default is until the target completes-a,--all-threadssamples every thread instead of only the main thread--realtime-statsprints sampling statistics during the run--blockingpauses the target during each sample for a guaranteed-consistent stack at the cost of perturbing the program
Profiling Modes
--mode selects what each sample counts:
| Mode | What it measures |
|---|---|
wall (default) |
Wall-clock time; every sample counts regardless of thread state |
cpu |
On-CPU time only; samples taken while a thread is running on a core |
gil |
GIL-holding time; samples taken only while the thread holds the GIL |
exception |
Exception-handling time; samples taken only while an exception is in flight |
Wall-clock mode includes time spent blocked on I/O or await. CPU mode excludes it. GIL mode is useful when diagnosing contention in multithreaded programs.
Async, Native, and Opcode Profiling
--async-awarereconstructs asyncio task stacks acrossawaitboundaries;--async-mode runningshows only the running task and--async-mode allincludes waiting tasks--nativeincludes<native>frames for C extensions and the interpreter itself--no-gcexcludes<GC>frames so garbage collection time does not dominate the report--opcodesrecords bytecode opcode information for instruction-level profiling--subprocessesfollowsmultiprocessingandsubprocesschildren automatically
Output Formats
Output formats are mutually exclusive flags:
| Flag | Output | Viewer |
|---|---|---|
--pstats (default) |
Text table to stdout, or a binary .pstats file |
pstats module, IDE viewers |
--collapsed |
Semicolon-separated stacks | External flame-graph tools (e.g. Brendan Gregg’s scripts) |
--flamegraph |
Self-contained interactive HTML | Any browser |
--diff-flamegraph BASELINE.bin |
Differential HTML flame graph | Any browser; color-codes regressions vs. a baseline binary |
--gecko |
Gecko-format JSON | Firefox Profiler |
--heatmap |
HTML directory with line-level overlays | Any browser |
--binary |
Compact binary; supports zstd compression | Use with replay to convert later |
-o/--output selects the output path, --browser auto-opens HTML output, and --compression {auto,zstd,none} controls the binary format.
The --live flag (available on both run and attach) opens a top-style terminal interface with sortable columns, a substring filter (/), per-thread navigation, and adjustable refresh rate.
Permissions
Sampling another process requires OS-level memory inspection, which has the same constraints as py-spy:
- Linux: ptrace capability, or root, or relax
kernel.yama.ptrace_scope - macOS: root, or a binary signed with the
com.apple.security.cs.debuggerentitlement - Windows: administrator, or
SeDebugPrivilege
A target that the profiler launched itself (python -m profiling.sampling run script.py) does not require elevated privileges on Linux or Windows; attaching to an already-running PID does. macOS requires elevated privileges in both cases.
Version Compatibility
The profiler must run on the same Python minor version as the target process. Pre-release builds must match exactly: 3.15.0a8 profiling 3.15.0a7 will fail. This is stricter than third-party profilers like py-spy that read process memory through OS APIs and support a range of CPython versions from a single profiler binary.
Pros
- Ships in the standard library; no
pip installand no separate binary. - Async-aware sampling reconstructs task stacks across
awaitboundaries. - Output formats include HTML flame graphs, Gecko JSON for the Firefox Profiler, line-level heatmaps, and differential flame graphs against a saved baseline.
- Live terminal mode is built in; previously this required
austin-tuior similar. - Differential flame graphs against a saved baseline are a first-class operation, useful for regression hunting in CI.
Cons
- Available only in Python 3.15 and newer. Older Pythons keep needing third-party tools.
- Profiler and target must share a Python minor version, and pre-release versions must match exactly.
- On macOS, even
python -m profiling.sampling run script.pyrequires elevated privileges; the OS does not allow process-memory inspection without them. - Same OS permission model as out-of-process profilers on the other platforms (ptrace on Linux, admin on Windows).
- The output formats unique to
profiling.sampling(heatmap directories, differential flame graphs) do not have third-party viewers yet, so existing flame-graph viewers like speedscope.app and the Perfetto UI do not consume them out of the box.
Learn More
- profiling.sampling: Statistical Profiler (official docs)
- profiling: Python profilers (the umbrella package)
- PEP 799: A dedicated profiling package
- Sampling vs deterministic profilers covers the trade-offs behind sampling and tracing designs
- py-spy reference covers the third-party sampling profiler that supports older Python versions
- Firefox Profiler reads the Gecko output format