Skip to content

Benchmarks ​

OasDelta against oasdiff, measured on GitHub-hosted runners by the Benchmarks & site workflow on every push to main and weekly. The numbers on this page come from the run that built it.

Linux 4 cores, GitHub-hosted runner

Speed-up, real-world APIs
2.8×
geometric mean, GitHub, Stripe and Kubernetes releases
Speed-up, all cases
2.6×
range 1.1× – 5.9×
Cases where oasdelta is faster
23 / 23
median wall time, start-up included
Identical output
22 / 23
stdout and exit code, byte for byte

One case (k8s-core changelog -f json) prints the same entries as oasdiff in a different order. oasdiff’s own order there changes from run to run (5 orders in 5 runs); OasDelta’s does not. oasdiff sorts changes with a comparison that ignores some of their fields, and leaves changes that differ only in those fields in Go’s random map order.

Windows 4 cores, GitHub-hosted runner

Speed-up, real-world APIs
2.4×
geometric mean, GitHub, Stripe and Kubernetes releases
Speed-up, all cases
2.2×
range 1.1× – 5.2×
Cases where oasdelta is faster
23 / 23
median wall time, start-up included
Identical output
22 / 23
stdout and exit code, byte for byte

One case (k8s-core changelog -f json) prints the same entries as oasdiff in a different order. oasdiff’s own order there changes from run to run (5 orders in 5 runs); OasDelta’s does not. oasdiff sorts changes with a comparison that ignores some of their fields, and leaves changes that differ only in those fields in Go’s random map order.

Results ​

Median wall time per invocation, process start-up included. Shorter is better. Hover or focus a row for details.

Small YAML (openapi-test1 → test3, 10 KB)
diff
2.7× faster
summary
2.4× faster
breaking
2.4× faster
changelog -f json
1.9× faster
changelog -f html
2.3× faster
Large YAML (synthetic, ~1.5 MB)
diff
3.2× faster
summary
2.9× faster
breaking
2.5× faster
changelog -f json
2.5× faster
changelog -f html
2.6× faster
Large JSON (synthetic, ~1.1 MB)
diff
2.6× faster
summary
2.7× faster
breaking
2.0× faster
changelog -f json
1.9× faster
changelog -f html
2.0× faster
GitHub REST API (YAML, ~10 MB, Aug → Nov 2026)
diff
2.5× faster
breaking
2.4× faster
changelog -f json
2.4× faster
Stripe API (JSON, ~8 MB, v2500 → v2598)
breaking
5.4× faster
changelog -f json
4.6× faster
Kubernetes core/v1 (JSON, ~2 MB, 1.36 → 1.37)
diff
5.9× faster
breaking
1.8× faster
changelog -f json
1.1× faster

What is measured ​

  • Binaries. oasdelta is the NativeAOT build that dotnet publish produces from this repository. oasdiff is the official release binary downloaded from oasdiff/oasdiff releases, with its checksum verified. Neither is rebuilt or tuned for the benchmark.
  • Inputs.
    • Small: openapi-test1.yaml → openapi-test3.yaml from the upstream test data, about 10 KB. This case is dominated by process start-up.
    • Large YAML / JSON: a synthetic API from tools/gen_bench_spec.py with 600 resources (1,200 paths, 2,400 operations, 650 schemas). The revision changes about one resource in ten: removed and added properties, type and enum changes, new required fields, removed endpoints and responses. So every stage, from parsing through diffing and checks to rendering, has work to do.
    • Real-world: two consecutive releases each of the GitHub REST API (YAML, ~10 MB), Stripe (JSON, ~8 MB) and Kubernetes core/v1 (JSON, ~2 MB), from OASDelta-TestData. Each source's license is recorded there.
  • Commands. diff, summary, breaking, changelog -f json and changelog -f html on the small and synthetic inputs. diff, breaking and changelog -f json on the real-world pairs, except Stripe's diff, whose YAML output is over 5 GB.
  • Timing. hyperfine 2.0 measures the wall time of the whole process, from spawn to exit, start-up included, because that is what a CI step pays. It starts each binary directly, with no shell in between, and discards its output. Each synthetic case gets 3 untimed warm-up runs, then 15 timed runs per binary. The real-world cases take seconds each, so they get 1 warm-up and 5 timed runs. hyperfine runs one binary's runs and then the other's, and which binary goes first alternates from case to case. The chart shows the median; the table adds the standard deviation.
  • Memory. On Linux, peak resident set size as hyperfine measures it, the highest over the timed runs. A process that hyperfine starts inherits hyperfine's own peak of a few MB, so no reading is lower than that. The environment table shows the reading for a program that does nothing. hyperfine cannot measure memory on Windows, so there each binary runs 3 more times, untimed, and the peak working set comes from GetProcessMemoryInfo, which counts only the process itself.
  • Correctness. Before timing, each binary runs once with its output saved to a file, and the two outputs and exit codes are compared byte for byte. A speed-up only counts if the answer is the same. Where the bytes differ but the lines, or the JSON array entries, are the same in another order, the case is reported as same entries, other order. Both binaries then run 5 times each, and the report records how many different outputs each one printed.

The harness is tools/bench.py. It runs the output check and drives hyperfine, measuring no time itself.

Environment ​

LinuxWindows
OSLinux 7.0.0-1019-azure (x86_64)Windows 2025Server (AMD64)
CPUINTEL(R) XEON(R) PLATINUM 8573C, 4 coresAMD EPYC 9V74 80-Core Processor, 4 cores
Gooasdiff version 1.35.0, 18.4 MB binaryoasdiff version 1.35.0, 18.9 MB binary
C#oasdelta version 1.0.0, 10.3 MB binaryoasdelta version 1.0.0, 10 MB binary
Runs3 warm-up + 15 timed, per case and binary3 warm-up + 15 timed, per case and binary
Timinghyperfine 2.0.0hyperfine 2.0.0
Memorypeak resident set size (hyperfine), highest of the timed runs; 1.4 MB for a program that does nothingpeak working set (GetProcessMemoryInfo), highest of 3 untimed runs
Real-world 1 warm-up + 5 timed, from OASDelta-TestData 15b2cb4 1 warm-up + 5 timed, from OASDelta-TestData 15b2cb4
MeasuredSat, 14 Nov 2026 13:56 UTCSat, 14 Nov 2026 13:57 UTC
Source278608a · workflow run278608a · workflow run

All numbers ​

Linux

InputCommandoasdiff (Go)oasdelta (C#)Speed-upGo peakC# peakOutput
smalldiff22.2 ms ± 1.6 ms8.3 ms ± 1.1 ms2.7×21.7 MB16.3 MBidentical
smallsummary18.9 ms ± 0.6 ms8.1 ms ± 1.1 ms2.4×20.3 MB16.2 MBidentical
smallbreaking21.1 ms ± 1.8 ms8.9 ms ± 1.1 ms2.4×21.9 MB16.8 MBidentical
smallchangelog -f json21.4 ms ± 1.1 ms11.3 ms ± 0.7 ms1.9×21.8 MB21.9 MBidentical
smallchangelog -f html21.5 ms ± 1.1 ms9.4 ms ± 0.7 ms2.3×21.8 MB17.3 MBidentical
large-yamldiff1.95 s ± 21.4 ms603 ms ± 5.4 ms3.2×317 MB201 MBidentical
large-yamlsummary1.69 s ± 14.7 ms577 ms ± 6.0 ms2.9×302 MB182 MBidentical
large-yamlbreaking1.77 s ± 13.7 ms696 ms ± 10.6 ms2.5×311 MB209 MBidentical
large-yamlchangelog -f json1.78 s ± 20.0 ms711 ms ± 7.7 ms2.5×315 MB214 MBidentical
large-yamlchangelog -f html1.78 s ± 12.2 ms691 ms ± 8.1 ms2.6×313 MB210 MBidentical
large-jsondiff970 ms ± 7.3 ms372 ms ± 6.8 ms2.6×223 MB193 MBidentical
large-jsonsummary734 ms ± 4.7 ms271 ms ± 5.1 ms2.7×103 MB184 MBidentical
large-jsonbreaking799 ms ± 16.3 ms398 ms ± 6.9 ms2.0×109 MB193 MBidentical
large-jsonchangelog -f json835 ms ± 7.8 ms436 ms ± 4.9 ms1.9×113 MB198 MBidentical
large-jsonchangelog -f html830 ms ± 9.9 ms421 ms ± 6.1 ms2.0×113 MB193 MBidentical
githubdiff7.38 s ± 83.5 ms2.96 s ± 18.1 ms2.5×1136 MB481 MBidentical
githubbreaking6.81 s ± 50.1 ms2.86 s ± 10.3 ms2.4×1003 MB481 MBidentical
githubchangelog -f json6.94 s ± 55.5 ms2.90 s ± 38.3 ms2.4×1118 MB481 MBidentical
stripebreaking17.26 s ± 58.0 ms3.18 s ± 39.9 ms5.4×457 MB543 MBidentical
stripechangelog -f json17.55 s ± 88.7 ms3.80 s ± 21.3 ms4.6×857 MB661 MBidentical
k8s-corediff4.11 s ± 129 ms691 ms ± 3.5 ms5.9×2060 MB221 MBidentical
k8s-corebreaking1.36 s ± 10.8 ms780 ms ± 30.4 ms1.8×218 MB241 MBidentical
k8s-corechangelog -f json1.96 s ± 17.2 ms1.73 s ± 12.6 ms1.1×593 MB458 MBsame entries, other order (distinct in 5 runs: oasdiff 5, oasdelta 1)

Windows

InputCommandoasdiff (Go)oasdelta (C#)Speed-upGo peakC# peakOutput
smalldiff30.6 ms ± 1.3 ms13.7 ms ± 0.2 ms2.2×20.0 MB13.1 MBidentical
smallsummary28.2 ms ± 1.1 ms13.4 ms ± 0.2 ms2.1×18.7 MB12.9 MBidentical
smallbreaking29.8 ms ± 1.2 ms14.5 ms ± 0.5 ms2.1×19.8 MB14.3 MBidentical
smallchangelog -f json29.5 ms ± 0.6 ms15.2 ms ± 0.4 ms1.9×19.8 MB14.5 MBidentical
smallchangelog -f html30.5 ms ± 0.6 ms15.2 ms ± 1.1 ms2.0×20.5 MB15.0 MBidentical
large-yamldiff1.67 s ± 19.9 ms685 ms ± 4.3 ms2.4×325 MB193 MBidentical
large-yamlsummary1.48 s ± 21.7 ms717 ms ± 58.1 ms2.1×307 MB193 MBidentical
large-yamlbreaking1.56 s ± 20.3 ms717 ms ± 14.9 ms2.2×303 MB194 MBidentical
large-yamlchangelog -f json1.56 s ± 24.2 ms724 ms ± 5.1 ms2.2×262 MB195 MBidentical
large-yamlchangelog -f html1.57 s ± 16.1 ms720 ms ± 6.8 ms2.2×299 MB196 MBidentical
large-jsondiff831 ms ± 9.5 ms344 ms ± 12.5 ms2.4×236 MB165 MBidentical
large-jsonsummary679 ms ± 11.0 ms321 ms ± 3.3 ms2.1×104 MB165 MBidentical
large-jsonbreaking724 ms ± 6.0 ms366 ms ± 2.8 ms2.0×103 MB172 MBidentical
large-jsonchangelog -f json729 ms ± 7.0 ms376 ms ± 2.6 ms1.9×106 MB179 MBidentical
large-jsonchangelog -f html735 ms ± 7.6 ms378 ms ± 4.5 ms1.9×107 MB183 MBidentical
githubdiff6.29 s ± 34.6 ms3.26 s ± 32.7 ms1.9×978 MB519 MBidentical
githubbreaking6.50 s ± 421 ms3.06 s ± 18.0 ms2.1×1080 MB519 MBidentical
githubchangelog -f json5.91 s ± 31.1 ms3.07 s ± 32.1 ms1.9×1063 MB519 MBidentical
stripebreaking14.79 s ± 194 ms2.87 s ± 17.1 ms5.2×457 MB505 MBidentical
stripechangelog -f json15.09 s ± 84.1 ms3.79 s ± 13.4 ms4.0×848 MB746 MBidentical
k8s-corediff2.77 s ± 49.7 ms717 ms ± 7.5 ms3.9×2164 MB216 MBidentical
k8s-corebreaking1.26 s ± 5.1 ms736 ms ± 7.1 ms1.7×209 MB233 MBidentical
k8s-corechangelog -f json1.74 s ± 17.6 ms1.66 s ± 7.4 ms1.1×576 MB458 MBsame entries, other order (distinct in 5 runs: oasdiff 5, oasdelta 1)

Reproduce it ​

sh
dotnet publish src/OasDelta -c Release -r linux-x64 -o out
mkdir specs && python3 tools/gen_bench_spec.py specs 600
python3 tools/bench.py --go "$(command -v oasdiff)" --cs out/oasdelta --specs specs --runs 15

This needs hyperfine 2.0 or later on PATH, or passed with --hyperfine. On Windows, use -r win-x64 and out\oasdelta.exe. Add --json results.json to write a file in the format this site reads; put it in docs/data/benchmarks/ and run npm run dev in docs/ to see it here.

Caveats ​

  • Shared CI runners are noisy. The medians are stable from run to run, but treat a difference under about 10% as a tie.
  • Until November 2026 the harness timed runs itself in Python and read each binary's output through a pipe, so those numbers included reading the output. On Linux its memory readings were too high: they included memory held by the Python process that started each binary, which kept every output it had read.
  • The synthetic inputs are built to exercise every stage of the pipeline, and the real-world pairs show three large public APIs. Your API's shape may still favour one implementation more or less.
  • Memory is a trade-off, not a free win. oasdelta peaks lower on small specs and on large YAML, but on large JSON oasdiff's peak is usually lower. .NET's GC is tuned here for throughput on a short-lived process (see Why C# and .NET), so check the memory view before running oasdelta in a tight container.

Apache License 2.0. OasDelta is a derivative work of oasdiff and is not affiliated with or endorsed by the oasdiff project.