A high-performance sampling profiler for Python with Speedscope and FlameGraph output.
- Low overhead — <1% CPU at 10ms sampling, suitable for production
- Mixed-mode profiling — Capture Python and C extension frames together
- Multi-threaded — Automatic profiling of all Python threads
- Memory-efficient — Stack aggregation for long-running profiles
- Cross-platform — Linux, macOS, Windows
- Python 3.9–3.14 — Including free-threaded builds (Linux & macOS)
- Zero dependencies — No runtime requirements
- Container-friendly — No
SYS_PTRACEcapability required
pip install spprofBuild from source:
pip install git+https://github.com/Periecle/spprof.gitimportspprofspprof.start()
# ... your code ...profile=spprof.stop()
profile.save("profile.json")
# View at https://www.speedscope.appwithspprof.Profiler(interval_ms=5) asp:
expensive_computation()
p.profile.save("profile.json")@spprof.profile(output_path="func.json")defheavy_work():
...| Function | Description |
|---|---|
start(interval_ms=10) | Begin profiling |
stop() | Stop and return Profile |
is_active() | Check if profiler is running |
stats() | Get live statistics |
# Option 1: Explicit registration (recommended for Linux)defworker():
spprof.register_thread()
try:
do_work()
finally:
spprof.unregister_thread()
# Option 2: Context managerdefworker():
withspprof.ThreadProfiler():
do_work()Capture C/C++ frames alongside Python for debugging extensions:
ifspprof.native_unwinding_available():
spprof.set_native_unwinding(True)
spprof.start()For long-running profiles with repetitive call patterns:
profile=spprof.stop()
aggregated=profile.aggregate()
print(f"Compression: {aggregated.compression_ratio:.1f}x")
aggregated.save("profile.json")profile.save("profile.json")Open at speedscope.app for interactive flame graphs.
profile.save("profile.collapsed", format="collapsed")Generate SVG with FlameGraph:
flamegraph.pl profile.collapsed > profile.svg| Parameter | Default | Description |
|---|---|---|
interval_ms | 10 | Sampling interval (1–1000ms) |
memory_limit_mb | 100 | Maximum buffer size |
output_path | None | Auto-save on stop |
| Interval | Overhead | Use Case |
|---|---|---|
| 1ms | ~5% | Short scripts, benchmarks |
| 10ms | <1% | Development (default) |
| 100ms | <0.1% | Production monitoring |
| Platform | Mechanism | Thread Sampling | Free-Threading |
|---|---|---|---|
| Linux | timer_create + SIGPROF | Per-thread CPU time | ✅ Supported |
| macOS | Mach thread suspension | All threads automatic | ✅ Supported |
| Windows | Timer queue + GIL | All threads automatic | — |
Linux: Per-thread CPU time sampling with explicit thread registration. Free-threaded Python 3.13+ is supported via speculative capture with validation (~0.0005% sample drop rate).
macOS: Full support for free-threaded Python (--disable-gil) via Mach-based thread suspension sampling.
git clone https://github.com/Periecle/spprof.git
cd spprof
pip install -e ".[dev]"pytest # Run tests
pytest --cov=spprof # With coverageruff check src/ tests/ # Lint
ruff format src/ tests/ # Format
mypy src/spprof # Type checkIf your workload completes too quickly, you may see zero samples. Ensure your workload runs at least 10x the sampling interval:
# For fast functions (< 100ms), use aggressive samplingspprof.start(interval_ms=1)
fast_function() # Must run > 10ms to capture samplesprofile=spprof.stop()If profile.dropped_count is high, samples are being lost due to buffer overflow:
# Increase memory limit for long-running profilesspprof.start(interval_ms=10, memory_limit_mb=200)
# Or reduce sampling frequencyspprof.start(interval_ms=100) # For production/long profilesIn containers with restricted syscalls (seccomp, cgroups v1), spprof automatically falls back to wall-time sampling. For full CPU-time profiling:
# Docker: Run with extended permissions (development only)
docker run --security-opt seccomp=unconfined myappSee Troubleshooting Guide for detailed solutions including custom seccomp profiles and Kubernetes configurations.
- Usage Guide — Detailed API documentation
- Architecture — Internal design
- Performance Tuning — Optimization guide
- Troubleshooting — Common issues and solutions
MIT License. See LICENSE for details.