StackGate
Statistically rigorous, function-level performance-regression detection from sampling profiles, for Python.
Python 3.15 introduces profiling.sampling ("Tachyon") in the standard library, enabling low-overhead sampling profiles and differential flame graphs. But comparing a single baseline run to a single candidate run produces false alarms: sampling noise and CI runner jitter easily swamp small 1–5% differences.
StackGate closes this gap:
- Takes multiple runs per side ($K$ baseline runs vs $M$ candidate runs).
- Models run-to-run noise and calculates a runner noise floor.
- Conducts permutation tests to determine whether the program got slower with rigorous statistical significance ($p$-value and confidence interval).
- Controls false discoveries across hundreds of functions using the Benjamini-Hochberg False Discovery Rate (FDR) procedure.
- Attributes the regression additively to specific functions and call paths (e.g.
parse_message: +0.28s, 80% of regression, q=0.0004). - Outputs automated PASS / FAIL / INCONCLUSIVE verdicts, Markdown PR comments for GitHub Actions, and structured JSON for CI gates.
What a user sees
$ stackgate compare profiles/base/ profiles/head/ --threshold 2.0
Verdict: FAIL (Performance Regression Detected)
Total wall time: 4.120s -> 4.470s (+8.50%, 95% CI +5.9% .. +11.2%, p=0.0003, K=8 vs 8)
Runner noise floor: 1.45% | Threshold: 2.00% | Baseline CV: 3.2%
Where it went (self time, FDR q < 0.05):
parse_message 0.610s -> 0.890s +0.280s (80.0% of regression) q=0.0004
json/decoder.py:raw_decode 0.150s -> 0.210s +0.060s (17.1% of regression) q=0.0018
New call paths (1):
+ main;handle_request;validate_schema;re.compile +0.050s
Not significant / below noise floor: 212 functions
Key Features
- Statistically Rigorous: Exact or Monte Carlo two-sample permutation tests, Welch's t-test, and bootstrap confidence intervals.
- Function-Level Attribution: Pinpoints the exact functions driving the slowdown and calculates each function's share of the total regression.
- Multiple-Comparison Protection: Applies the Benjamini-Hochberg (BH) procedure to control False Discovery Rate (FDR) when testing hundreds of functions simultaneously.
- Empirical A/A Calibration: Measures your CI runner's natural jitter and determines the recommended sample size $K$ to reliably detect a given effect size.
- Zero External Dependencies: Pure Python standard library core (
math,statistics,random,argparse). Installs anywhere in milliseconds with zero supply-chain risk. - Universal Profile Ingestion: Ingests Brendan Gregg collapsed/folded stacks, Python 3.15 Tachyon profiles,
py-spyprofiles, Austin, and custom JSON. - CI-First Automation: Built-in Markdown PR comment generator, color terminal reports, and exit codes (0 = PASS, 1 = FAIL, 2 = INCONCLUSIVE).
- Pytest Integration: Includes a
stackgate_benchmarkfixture andassert_no_regressionassertion helper.
Installation
pip install stackgate
StackGate requires Python 3.10+ and has zero required third-party dependencies.
Quickstart
1. Command-Line Interface (CLI)
Comparing Profile Directories
Collect baseline runs (e.g., from main) and candidate runs (from your PR), then compare them:
# Compare directories of folded profiles
stackgate compare profiles/baseline/ profiles/candidate/ --threshold 2.0
# Generate a GitHub PR comment markdown report
stackgate compare profiles/base/ profiles/head/ --format markdown -o pr_comment.md
# Output machine-readable JSON for CI pipelines
stackgate compare profiles/base/ profiles/head/ --format json -o result.json
Empirical A/A Calibration
Measure your test runner's noise floor and determine how many runs ($K$) you need:
stackgate calibrate profiles/baseline/
Output:
A/A Calibration on 10 runs (100 splits):
Empirical False Positive Rate: 4.0% (target: 5.0%)
Run-to-run noise floor (95th pct): 1.62%
Duration CV: 2.84%
Recommended sample sizes for 80% power: [2.0%: K=16, 3.0%: K=7, 5.0%: K=3, 10.0%: K=3]
Recording Profiles Directly
StackGate can run your benchmark or CLI command multiple times and record folded profiles:
stackgate record --runs 5 --out profiles/candidate -- python my_script.py --benchmark
2. Python Programmatic API
import stackgate
# Ingest profile sets from directory or files
baseline = stackgate.parse_directory("profiles/baseline/")
candidate = stackgate.parse_directory("profiles/candidate/")
# Perform noise-aware comparison
result = stackgate.compare(
baseline=baseline,
candidate=candidate,
threshold=2.5, # 2.5% regression threshold
alpha=0.05,
metric="self",
)
print(f"Verdict: {result.verdict}")
print(f"Total Change: {result.total_pct_change:+.2f}% (p={result.total_p_value:.4f})")
print(f"95% CI: [{result.ci_lower:+.1f}%, {result.ci_upper:+.1f}%]")
for fn in result.regressed_functions:
print(f" {fn.function}: +{fn.delta:.3f}s ({fn.attribution_pct:.1f}% share, q={fn.adjusted_p_value:.4f})")
# Render GitHub PR comment
pr_markdown = result.to_markdown()
# Programmatic CI assertion
stackgate.assert_no_regression(baseline, candidate, threshold=2.5)
3. Pytest Integration
import pytest
from stackgate import assert_no_regression
def test_pipeline_throughput(stackgate_benchmark):
# Runs the callable 5 times under the sampling profiler
candidate_profile = stackgate_benchmark(run_heavy_computation, runs=5)
# Assert against golden baseline
assert_no_regression(
baseline="baselines/heavy_computation/",
candidate=candidate_profile,
threshold=3.0, # Fail if >3% slower with p < 0.05
)
GitHub Actions CI Workflow
Add this to .github/workflows/perf-gate.yml:
name: Performance Gate
on:
pull_request:
branches: [main]
jobs:
performance:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.13"
- name: Install dependencies
run: |
pip install stackgate .
- name: Profile Candidate Branch
run: |
stackgate record --runs 8 --out profiles/candidate -- python -m myapp.benchmark
- name: Checkout Baseline
run: |
git checkout origin/main
- name: Profile Baseline
run: |
stackgate record --runs 8 --out profiles/baseline -- python -m myapp.benchmark
- name: Run StackGate Comparison
id: gate
run: |
stackgate compare profiles/baseline profiles/candidate \
--threshold 3.0 \
--format markdown \
--output comment.md
- name: Comment PR
if: always()
uses: actions/github-script@v7
with:
script: |
const fs = require('fs');
if (fs.existsSync('comment.md')) {
const body = fs.readFileSync('comment.md', 'utf8');
github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: body
});
}
Statistical Methodology
| Component | Technique | Rationale |
|---|---|---|
| Total Regression Test | Non-parametric Permutation Test (exact for $N \le 16$, Monte Carlo $B=10,000$ for $N > 16$) | Profile durations violate normality under JIT/GC pauses. Permutation tests make zero distributional assumptions. |
| Confidence Interval | Bootstrap Resampling ($B=5,000$) | Delivers realistic error bounds on relative percentage change $% \Delta$. |
| Function Attribution | Benjamini-Hochberg (BH) FDR Procedure | Profiling inspects hundreds of functions. Standard $p < 0.05$ produces multiple false positives. BH controls false discoveries at $q < 0.05$. |
| Additive Decomposition | Leaf-frame self-time delta partitioning | Attributions cleanly sum: $\sum_f \Delta_f \approx \Delta_{\text{total}}$. |
| Noise Floor Calibration | Empirical A/A permutation split | Prevents alerting when observed differences are within CI runner background jitter. |
Supported Formats
- Brendan Gregg Collapsed/Folded Stacks:
main;handle_request;parse_json 142 main;handle_request;validate 45 main;worker;fetch_sql 320
- Python 3.15 Tachyon:
profiling.samplingrecord/replay output. py-spy: Folded output viapy-spy record -f folded.- Austin: Folded format (
austin -P ...). - StackGate JSON Profiles: Serialized JSON representation.
Comparison with Existing Tools
| Tool | Granularity | Noise Model | Verdict / CI Gate | Function Attribution | Zero Deps? |
|---|---|---|---|---|---|
Tachyon --diff-flamegraph |
Function/Stack | None (1 run) | No | Visual only | Standard Library |
Brendan Gregg difffolded.pl |
Stack | None (1 run) | No | Visual only | Perl script |
| pytest-benchmark / pyperf | Benchmark | Yes | Varies | Benchmark only | Needs external packages |
| Continuous Profilers (Pyroscope) | Flamegraph | None | Dashboard only | Visual UI | Hosted / Go backend |
| StackGate | Function + Path | Yes (Multi-Run) | Yes (Automated Gate) | Yes (Additive FDR) | Yes (Pure Python) |
Author & Contributing
- Author: Samarth Agrawal (agrawalsamarth0206@gmail.com)
- Contributions: Pull requests, bug reports, and suggestions are welcome! Please open an issue on GitHub.
License
Distributed under the MIT License. Copyright © 2026 Samarth Agrawal.
Metadata
Release files for stackgate 0.1.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| stackgate-0.1.1.tar.gz | 55.4 kB | With provenance | Details for stackgate-0.1.1.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| stackgate-0.1.1-py3-none-any.whl | Python 3 | none | any | Details for stackgate-0.1.1-py3-none-any.whl |
Total release size: 96.7 kB
Release files / stackgate-0.1.1.tar.gz
| Download URL | stackgate-0.1.1.tar.gz |
|---|---|
| Size | 55.4 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
1028aae9d9dae54db08273321dab96b4f37df4c06b9f8a950172539b94ca815d
|
|
BLAKE2b-256 checksum How to use checksums |
39356bbb0030858a316dc485733a4318565d33cf9c7f115165a9925bccc59f4e
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 22, 2026.
Transparency logRelease files / stackgate-0.1.1-py3-none-any.whl
| Download URL | stackgate-0.1.1-py3-none-any.whl |
|---|---|
| Size | 41.2 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
7b77fc4bd76bb6a35f3f5d4feb81de9851f2991f84b92bbef4f8319c5b851794
|
|
BLAKE2b-256 checksum How to use checksums |
82caeba5ead8938296d70a08200605a95e85c273ada0318c3bec551f6aedfad5
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 22, 2026.
Transparency logSecurity for stackgate 0.1.1
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for stackgate-0.1.1.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 22, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.