Documentation

Submit benchmark data with one CLI and the framework your team already uses.

Perfall accepts benchmark artifacts from seven supported adapters. Install perfall-cli from PyPI once, generate the artifact for your stack, and upload it with your project API key.

  • PyPI install
  • 7 supported adapters
  • PR regression gate

Quickstart

1. Install the upload CLI

Install perfall-cli once from PyPI on the machine that will publish benchmark results.

pip install perfall-cli

2. Create a project and copy its API key

Open the Projects page, create a project, and reveal the API key when you are ready to wire your benchmark job.

3. Generate one supported artifact

Pick the framework below that matches your stack and run its export command to create a JSON or JSONL artifact.

4. Upload the artifact

Run perfall ingest <adapter> with the artifact path and your project API key, then return after the next run to compare trends.

Fastest first signal

Want to confirm the CLI, API key, and dashboard path before wiring a real benchmark suite? Download the sample pytest-benchmark artifact and upload it with your project API key.

perfall ingest pytest-benchmark \
  --file pytest-benchmark-sample.json \
  --api-key "$PERFALL_API_KEY"

Supported frameworks

Choose the adapter that matches your benchmark output

The adapters below map directly to the current perfall-cli command surface. Use the section links to jump to the copy-paste commands for your stack.

Python

pytest-benchmark

Use pytest-benchmark's JSON export when your benchmark suite already lives in pytest.

Minimal benchmark
def test_sort(benchmark):
    values = [3, 1, 2]
    benchmark(sorted, values)
Generate artifact
pytest --benchmark-only --benchmark-json benchmark.json
Upload to Perfall
perfall ingest pytest-benchmark --file benchmark.json --api-key "$PERFALL_API_KEY"
  • Install the benchmark plugin in your project with `pip install pytest-benchmark`.
  • Perfall reads the JSON generated by pytest-benchmark's `--benchmark-json` option.

Go

go test benchmarks

Use Go's built-in benchmark support and upload the JSON event stream from `go test -bench`.

Minimal benchmark
func BenchmarkSort(b *testing.B) {
    for i := 0; i < b.N; i++ {
        values := []int{3, 1, 2}
        sort.Ints(values)
    }
}
Generate artifact
go test -bench . -benchmem -json ./... > go-benchmark.jsonl
Upload to Perfall
perfall ingest go-test --file go-benchmark.jsonl --api-key "$PERFALL_API_KEY"
  • Go benchmarks are part of the standard `testing` package, so you do not need a separate benchmark framework.
  • Perfall maps the common `ns/op`, `B/op`, and `allocs/op` metrics from the JSON output.

Java

JMH

Use JMH when you need stable JVM benchmark output and export the run as JSON.

Minimal benchmark
@State(Scope.Thread)
public class SortBenchmark {
    @Benchmark
    public int[] sort() {
        int[] values = {3, 1, 2};
        Arrays.sort(values);
        return values;
    }
}
Generate artifact
java -jar benchmarks.jar -rf json -rff jmh-result.json
Upload to Perfall
perfall ingest jmh --file jmh-result.json --api-key "$PERFALL_API_KEY"
  • Set up JMH in your Maven or Gradle build so you can run a benchmark jar before using the command above.
  • Perfall reads JMH primary metrics and secondary metrics from the JSON artifact.

Rust

Criterion.rs via cargo-criterion

Use Criterion.rs with cargo-criterion when you want a machine-readable JSONL stream.

Minimal benchmark
fn sort_benchmark(c: &mut Criterion) {
    c.bench_function("sort_u64", |b| {
        b.iter(|| {
            let mut values = vec![3_u64, 1, 2];
            values.sort();
        })
    });
}
Generate artifact
cargo criterion --message-format=json > cargo-criterion.jsonl
Upload to Perfall
perfall ingest criterion --file cargo-criterion.jsonl --api-key "$PERFALL_API_KEY"
  • Install cargo-criterion once with `cargo install cargo-criterion` if your environment does not already provide it.
  • Perfall reads one JSON object per line from the cargo-criterion output stream.

JavaScript / TypeScript

Vitest benchmark mode

Use Vitest benchmark mode when you want benchmark cases alongside your existing JS or TS tests.

Minimal benchmark
import { bench, describe } from 'vitest';

describe('sort', () => {
  bench('sort numbers', () => {
    [3, 1, 2].sort();
  });
});
Generate artifact
npx vitest bench --outputJson vitest-benchmark.json
Upload to Perfall
perfall ingest vitest --file vitest-benchmark.json --api-key "$PERFALL_API_KEY"
  • Install Vitest in your project with `npm install -D vitest`.
  • Perfall reads the JSON written by Vitest's `--outputJson` option.

C++

Google Benchmark

Use Google Benchmark JSON output when your native benchmark suite already builds a benchmark binary.

Minimal benchmark
static void BM_Sort(benchmark::State& state) {
  for (auto _ : state) {
    std::array<int, 3> values{3, 1, 2};
    std::sort(values.begin(), values.end());
  }
}
BENCHMARK(BM_Sort);
BENCHMARK_MAIN();
Generate artifact
./benchmarks --benchmark_out=google-benchmark.json --benchmark_out_format=json
Upload to Perfall
perfall ingest google-benchmark --file google-benchmark.json --api-key "$PERFALL_API_KEY"
  • Build your benchmark binary with Google Benchmark enabled before running the export command.
  • Perfall reads both timing fields and numeric counter fields from the generated JSON.

.NET

BenchmarkDotNet

Use BenchmarkDotNet with a JSON exporter when you want structured benchmark output from a .NET project.

Minimal benchmark
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Exporters.Json;

[JsonExporterAttribute.Brief]
public class MyBenchmarks
{
    [Benchmark]
    public void Sort()
    {
        var values = new[] { 3, 1, 2 };
        Array.Sort(values);
    }
}
Generate artifact
dotnet run -c Release --project benchmarks.csproj
Upload to Perfall
perfall ingest benchmarkdotnet --file BenchmarkDotNet.Artifacts/results/MyBenchmarks-report-brief.json --api-key "$PERFALL_API_KEY"
  • Add BenchmarkDotNet to your project with `dotnet add package BenchmarkDotNet` and include a JSON exporter attribute in your benchmark class.
  • Perfall reads the generated `*-report-brief.json` artifact.

CI regression gate

Fail CI when a benchmark regresses past your threshold

Perfall can upload the current run, compare it with the latest baseline-branch sample for each smaller-is-better metric, print a concise summary, and exit non-zero when a regression exceeds your configured percentage.

CLI example
perfall ingest pytest-benchmark \
  --file benchmark.json \
  --api-key "$PERFALL_API_KEY" \
  --baseline-branch main \
  --fail-on-regression-percent 5
GitHub Actions example
name: Perfall benchmark gate

on:
  push:
    branches: [main]
  pull_request:

jobs:
  benchmarks:
    runs-on: ubuntu-latest
    env:
      PERFALL_API_KEY: ${{ secrets.PERFALL_API_KEY }}
      PERFALL_URL: ${{ vars.PERFALL_URL }}
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-python@v5
        with:
          python-version: "3.12"
      - run: |
          python -m pip install --upgrade pip
          python -m pip install perfall-cli pytest pytest-benchmark
          pytest --benchmark-only --benchmark-json benchmark.json
      - run: |
          command=(
            perfall ingest pytest-benchmark
            --file benchmark.json
            --api-key "$PERFALL_API_KEY"
          )
          if [ -n "${PERFALL_URL:-}" ]; then
            command+=(--perfall-url "$PERFALL_URL")
          fi
          if [ "$GITHUB_EVENT_NAME" = "pull_request" ]; then
            command+=(
              --baseline-branch "${{ github.event.pull_request.base.ref }}"
              --fail-on-regression-percent 5
            )
          fi
          "${command[@]}"
  • Required secret: PERFALL_API_KEY. Optional repository variable: PERFALL_URL when you need to target a non-default Perfall deployment.
  • The public starter installs perfall-cli from PyPI directly and does not depend on any repo-local GitHub Action files.
  • The baseline comparison uses the latest sample already stored on the requested baseline branch for the same identifier.
  • For pull-request workflows, set baseline-branch to the PR base branch, such as GitHub Actions github.event.pull_request.base.ref.
  • When the threshold is exceeded, the CLI exits non-zero and names the failing metric, current value, baseline value, and percent delta.
  • When the threshold is not exceeded, the CLI still prints the comparison summary so the benchmark decision stays visible in CI logs.
  • Throughput-style larger-is-better metrics such as ops/s are skipped by this MVP gate.

Manual API fallback

The CLI is the recommended path, but direct integrations can POST normalized JSON to the ingest API when they need more control.

Endpoint
POST https://perfall.it/ingest/v1/samples
Minimal payload
{
  "api_key": "project-api-key",
  "run": {
    "tool": "pytest-benchmark",
    "branch": "main"
  },
  "samples": [
    {
      "identifier": "bench.sort.mean",
      "value": 10.4,
      "timestamp": "2026-03-19T05:30:00+00:00"
    }
  ]
}
  • Required fields: api_key and at least one samples[] entry.
  • Each sample needs an identifier and a numeric value.
  • Success responses include status, project_id, accepted_samples, and identifiers.
  • Quota rejection uses the deterministic error code account_daily_quota_exceeded.
  • Advanced fallback endpoint: POST https://perfall.it/ingest/submit.

Need an API key before you can upload?

Create an account, create a project, and copy its API key from the Projects page before running the upload command for your framework.