CPython Performance Profiling and Acceleration
Parent: Programming Languages · researched 2026-06-01T05:19:59.013Z· 18 sources · 8 concepts · skill cpython-performance-profiling
> Hub reference under programming-languages. Created via /dr (2026-06-01). Sources: official Python docs (profile/pstats), project docs/GitHub (py-spy, Scalene, memray, pytest-memray, Cython), the Sca
CPython Performance Profiling and Acceleration
- > Hub reference under programming-languages. Created via /dr (2026-06-01). Sources: official Python docs (profile/pstats), project docs/GitHub (py-spy, Scalene, memray, pytest-memray, Cython), the Scalene arXiv paper, pyperf docs. [source]
- Performance work in Python is two phases, in this order: measure (profile and benchmark to find the real bottleneck) then accelerate (fix it, native last). The most-violated rule is to optimize before profiling; the second is to trust measurements that the profiler's own overhead has distorted. Pick the tool by the question you are asking. [source]
1. Deterministic profiling — cProfile / profile / pstats
- Monitors every call/return/exception with precise timing. Use the C-extension cProfile (low overhead); profile is the pure-Python, hookable, much slower twin (used for calibration). [source]
- Columns: ncalls (call count); tottime (in-function, excludes subcalls - sort to find hot loops); cumtime (cumulative incl. subcalls - find expensive chains); two percall. SortKey enum (3.7+): CALLS, CUMULATIVE, FILENAME, LINE, NAME, NFL, PCALLS, STDNAME, TIME. Stats: add() (merge), print_callers/callees(), get_stats_profile() (3.9+). Calibration: bias = profile.Profile().calibrate(10000). Visualize with snakeviz / gprof2dot / tuna. Tradeoff: per-call overhead distorts many-tiny-call workloads - use sampling for production. [source]
2. Statistical / sampling profilers — py-spy, Austin
- py-spy (Rust, benfred; rbspy lineage) profiles a process you can't/won't instrument, including production. Separate process reading target memory (process_vm_readv / vm_read / ReadProcessMemory) - zero code changes, very low overhead. [source]
- Flags: --rate, --duration, --native (C/C++/Cython frames), --gil (only GIL-holding threads), --subprocesses, --idle, --nonblocking. Output: flamegraph SVG (default), speedscope, raw. Permissions: spawning is unprivileged; attaching needs sudo/ptrace on Linux (ptrace_scope), root on macOS, SYS_PTRACE in Docker/K8s. Austin is a sibling C frame-stack sampler for the same flamegraph/speedscope pipeline. [source]
3. Scalene — line-level CPU+GPU+memory, native separation
- plasma-umass profiler that separates Python vs native (C/C++) vs system (I/O) time plus GPU + memory at per-line granularity - instantly answers "is this even optimizable in Python?" (mostly system time = I/O-bound; mostly native = inside a C library). [source]
- Flags: --cpu-only/--gpu/--memory, --reduced-profile, --profile-only/-exclude, thresholds. Target with @profile or scalene_profiler.start()/stop(). Copy volume (MB/s) flags costly silent C↔Python / CPU↔GPU copies. Low overhead via sampling + signal handlers + native stack stitching (~10–20%). AI suggestions (⚡/💥) via Bedrock/Azure/OpenAI/Ollama; experimental --memory-leak-detector. [source]
4. memray — allocation-level memory profiling (Bloomberg)
- Tracks allocations in Python, native extensions, and the interpreter by intercepting allocators. Linux/macOS only (no Windows). [source]
- --native adds C/C++ frames (essential for numpy/pandas). pytest-memray: --memray + @pytest.mark.limit_memory("100 MB"). Default = high-watermark (peak); --leaks/--temporal switch modes. [source]
5. line_profiler / kernprof — per-line CPU
- Reports Hits / Time / Per Hit / % Time per source line. py-heat = heatmap. Real overhead - scope to the one function under investigation. [source]
6. Benchmarking — measure the fix, not the noise
- timeit - micro-snippets (python -m timeit "..."); weak isolation. [source]
- pyperf (PSF) - rigorous: multi-process, warmup (skips first value), mean±stdev, keeps GC, pyperf system tune to suppress outliers. Use for any "A vs B" claim that matters. [source]
- pytest-benchmark - benchmarks in the test suite, regression tracking; pair with CI perf budgets. [source]
7. Flame-graph interpretation
- Width = cost (time, or bytes for memray). X-axis is not time order in a classic flame graph - it's grouped/sorted stacks. [source]
- Self time (frame's own bar minus children) vs cumulative (whole stack width). Wide frame + narrow children = work is here; wide children = cost is below. [source]
- speedscope views: Time Order, Left Heavy (best for biggest contributors), Sandwich. py-spy --gil shows real on-CPU Python. [source]
8. The native-acceleration ladder (native is the LAST resort)
- Algorithm / data structure - biggest wins (O(n²)→O(n log n), set/dict membership, generators). [source]
- Builtins / vectorization - push loops into C (comprehensions, str.join, itertools, NumPy vectorized ops). [source]
- Concurrency - asyncio/threads for I/O; processes (or free-threaded 3.13t+) for CPU-bound. [source]
- Native compilation of the proven hotspot: [source]
- Cython: use typed memoryviews (double[:, ::1]) for fast array access (unlocks nogil); prange(..., nogil=True) for OpenMP; run cython -a and drive yellow (Python-object) lines white. Pure-Python mode keeps source runnable as plain .py. [source]
Anti-patterns and gotchas
- Optimizing before profiling - intuition about Python hotspots is usually wrong. [source]
- Trusting overhead-distorted numbers - cProfile inflates many-small-call code; line_profiler inflates the line under test. Cross-check with py-spy/Scalene before a rewrite. [source]
- Optimizing the wrong layer - system-time (I/O) or native-time (C library) lines won't get faster from Python changes; Scalene's split catches this. [source]
- Micro-benchmarking without warmup/isolation - use pyperf for decisions. [source]
- Reaching for native too early - exhaust algorithm/vectorization/concurrency first. [source]
- Wall-clock vs CPU time - a sleep/network-bound function isn't a JIT candidate. [source]
- Forgetting --native - hides the C-extension frames where cost often lives (numpy/pandas/torch). [source]
- memray on Windows - unsupported; use py-spy or tracemalloc there. [source]
References (2026-06-01)
- Python docs - Profilers: https://docs.python.org/3/library/profile.html • pstats: https://docs.python.org/3/library/pstats.html [source]
- py-spy: https://github.com/benfred/py-spy [source]
- Scalene: https://github.com/plasma-umass/scalene • arXiv: https://arxiv.org/pdf/2212.07597 [source]
- memray: https://github.com/bloomberg/memray • https://bloomberg.github.io/memray/ • pytest-memray: https://github.com/bloomberg/pytest-memray [source]
- Cython parallelism: https://cython.readthedocs.io/en/latest/src/userguide/parallelism.html • memoryviews: https://docs.cython.org/en/latest/src/userguide/memoryviews.html [source]
- pyperf: https://pyperf.readthedocs.io/ [source]
- Cython/Numba/PyO3 comparison (Witt): https://wittgeo.medium.com/boost-python-performance-with-cython-numba-and-pyo3-486d59d8c2c6 [source]
Children
- Deterministic profiling (cProfile/profile + pstats, SortKey, ncalls/tottime/cumtime, calibration, snakeviz/gprof2dot) (frontier)
- Statistical/sampling profilers (py-spy record/top/dump, --native/--gil/--subprocesses, speedscope/flamegraph, Austin) (frontier)
- Scalene (line-level CPU+GPU+memory, Python-vs-native-vs-system separation, copy-volume, AI suggestions) (frontier)
- memray (Bloomberg allocation profiler, native tracking, flamegraph/table/tree, live mode, leaks/temporal, pytest-memray) (frontier)
- line_profiler/kernprof per-line CPU and py-heat (frontier)
- Benchmarking (timeit, pyperf, pytest-benchmark, CI perf budgets) (frontier)
- Flame-graph interpretation (self vs cumulative, speedscope views) (frontier)
- Native-acceleration ladder (Cython cdef/typed-memoryviews/nogil/prange, Numba @njit, mypyc, PyO3/Rust, ctypes/cffi) (frontier)
Frontier under this node: Benchmarking (timeit, pyperf, pytest-benchmark, CI perf budgets), Deterministic profiling (cProfile/profile + pstats, SortKey, ncalls/tottime/cumtime, calibration, snakeviz/gprof2dot), Flame-graph interpretation (self vs cumulative, speedscope views), Native-acceleration ladder (Cython cdef/typed-memoryviews/nogil/prange, Numba @njit, mypyc, PyO3/Rust, ctypes/cffi), Scalene (line-level CPU+GPU+memory, Python-vs-native-vs-system separation, copy-volume, AI suggestions), Statistical/sampling profilers (py-spy record/top/dump, --native/--gil/--subprocesses, speedscope/flamegraph, Austin), line_profiler/kernprof per-line CPU and py-heat, memray (Bloomberg allocation profiler, native tracking, flamegraph/table/tree, live mode, leaks/temporal, pytest-memray)