# 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, `dump` for a single stack snapshot, and `replay` for converting saved binary profiles to other output formats.

The new module sits inside the broader [PEP 799](https://peps.python.org/pep-0799/) 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 reached beta 1 on 7 May 2026, the milestone after which no new features are accepted into the release. The final release is scheduled for October 2026. The CLI surface documented here tracks the current 3.15 development tree.

[uv](https://pydevtools.com/handbook/reference/uv.md) installs alpha and beta Pythons with the same command it uses for stable releases:

```bash
uv python install 3.15        # pulls the latest published 3.15 pre-release
uv python install 3.15.0b1    # pins a specific pre-release
```

`uv python install 3.15` resolves to the latest version matching that minor, so it picks up newer betas and the final release as they ship. uv's Python catalog tracks [python-build-standalone](https://github.com/astral-sh/python-build-standalone), which typically publishes a build a few days after each upstream pre-release; if `uv python list --all-versions | grep 3.15` does not yet show `3.15.0b1`, the build is still in flight and the latest published alpha (currently `3.15.0a8`) is the most recent installable pre-release. To pin a project virtualenv to a specific pre-release once published, use `uv venv --python 3.15.0b1`. The [Version Compatibility](#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](https://pydevtools.com/handbook/reference/py-spy.md) and [pyinstrument](https://pyinstrument.readthedocs.io/)), 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](https://pydevtools.com/handbook/explanation/sampling-vs-deterministic-profilers.md).

## 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 |
| `dump` | Take a single snapshot of a running process's call stack |
| `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-rate` accepts a number or a unit suffix (`10000`, `10khz`, `10k`)
- `-d`, `--duration` runs for a fixed number of seconds; default is until the target completes
- `-a`, `--all-threads` samples every thread instead of only the main thread
- `--realtime-stats` prints sampling statistics during the run
- `--blocking` pauses 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-aware` reconstructs asyncio task stacks across `await` boundaries; `--async-mode running` shows only the running task and `--async-mode all` includes waiting tasks
- `--native` includes `<native>` frames for C extensions and the interpreter itself
- `--no-gc` excludes `<GC>` frames so garbage collection time does not dominate the report
- `--opcodes` records bytecode opcode information for instruction-level profiling
- `--subprocesses` follows `multiprocessing` and `subprocess` children 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](https://profiler.firefox.com/) |
| `--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](https://pydevtools.com/handbook/reference/py-spy.md):

- Linux: ptrace capability, or root, or relax `kernel.yama.ptrace_scope`
- macOS: root, or a binary signed with the `com.apple.security.cs.debugger` entitlement
- 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.0a6` profiling `3.15.0a5` will fail. This is stricter than third-party profilers like [py-spy](https://pydevtools.com/handbook/reference/py-spy.md) 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 install` and no separate binary.
- Async-aware sampling reconstructs task stacks across `await` boundaries.
- 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-tui` or 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.py` requires 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](https://www.speedscope.app/) and the [Perfetto UI](https://ui.perfetto.dev/) do not consume them out of the box.

## Learn More

- [profiling.sampling: Statistical Profiler](https://docs.python.org/3.15/library/profiling.sampling.html) (official docs)
- [profiling: Python profilers](https://docs.python.org/3.15/library/profiling.html) (the umbrella package)
- [PEP 799: A dedicated profiling package](https://peps.python.org/pep-0799/)
- [Sampling vs deterministic profilers](https://pydevtools.com/handbook/explanation/sampling-vs-deterministic-profilers.md) covers the trade-offs behind sampling and tracing designs
- [py-spy reference](https://pydevtools.com/handbook/reference/py-spy.md) covers the third-party sampling profiler that supports older Python versions
- [Firefox Profiler](https://profiler.firefox.com/) reads the Gecko output format
