Benchmarks
How fast is FerrFlow?
Side-by-side comparison against the JavaScript ecosystem release tools, measured with hyperfine on the same hardware, the same commits, and the same fixtures. Numbers refresh on every FerrFlow release.
Last update: September 7, 2026, ferrflow v7.21.1, binary size 8.0 MB
Median of 30 hyperfine runs, linear scale, cold cache, FerrFlow pinned to --jobs 1, lower is better. The pale segment at the foot of each bar is that tool's startup (process spawn, runtime boot, module loading) measured against a one-package one-commit floor fixture; the coloured segment above it is the work. The total is what you wait for; the split is where it goes.
| Tool | 1 × 100 | 10 × 100 | 50 × 500 | 100 × 1k | 50 × 5k | 200 × 10k | 50 × 300, graph |
|---|---|---|---|---|---|---|---|
| ferrflow | 7 ms | 10 ms | 25 ms | 54 ms | 193 ms | 583 ms | 22 ms |
| changesets | 536 ms | 568 ms | 626 ms | 694 ms | 678 ms | 636 ms | 638 ms |
| commit-and-tag-version | 481 ms | 489 ms | 528 ms | 600 ms | 941 ms | 1.07 s | 517 ms |
| semantic-release | 1.44 s | 1.35 s | 1.48 s | 1.58 s | 2.35 s | 2.45 s | 1.54 s |
| standard-version | 550 ms | 534 ms | 606 ms | 652 ms | 1.13 s | 1.34 s | 600 ms |
How we measure
- Hyperfine with 3 warmup runs and 30 timed runs per (fixture × tool × command) cell. Median latency reported.
- Cold cache. FerrFlow memoises its analysis under
.git/ferrflow-cache. That cache is dropped before every timed run, so the comparison measures the work itself. None of the other tools has an equivalent to hide behind. The warm numbers are reported separately below. - Peak RSS via
/usr/bin/time -von a single subsequent run. - Fixtures are generated from FerrLabs/Fixtures with deterministic conventional-commit history (no flaky inputs). The commit count in each fixture label is the number of commits since the last release tag: the exact window FerrFlow walks to compute a bump, not the repository's total history.
- All commands are dry-run release planning. Every tool computes the next version and release notes without writing anything:
ferrflow release --dry-run,semantic-release --dry-run --no-ci,standard-version --dry-run,commit-and-tag-version --dry-run, etc. The same operation for every tool, so we're comparing the planning cost, not the IO cost of actually mutating files. - Single-threaded comparison. FerrFlow runs with
--jobs 1in the chart above, so it's measured like the single-threaded JS tools. Its real-world parallel numbers are reported separately. - One row isn't like-for-like, and we'd rather say so than quietly win. changesets plans releases from
.changeset/*.mdfiles you write by hand, and never from commits. Six of our seven fixtures give it none, so it reports no packages to be bumped and stops. Its time tracks package count, not history: it barely moves across a 100× range of commits, because it isn't reading them. So where we come out ahead, most of the gap isnpxand Node startup rather than release planning. Strip the startup out, as the split in the chart above does, and on the largest fixture changesets spends less time working than we do, because it is doing less. Read that row as a difference in job, not a verdict. The50 × 300, graphfixture is the opposite case: there we resolve a 50-package dependency cascade and recover missed releases across the history, while the others emit one bump and have no notion of a dependency graph. - Reproducible: the full pipeline is in FerrLabs/Benchmarks; results land as artifacts on every
mainpush and in each release notes.
Parallel mode
The comparison pins FerrFlow to a single thread (--jobs 1) so it's apples-to-apples with the single-threaded JS tools. In real CI it is free to use every core, so this table reports what that actually buys. Here's the same release --dry-run on all 4 cores of the benchmark runner.
| Fixture | 1 thread | 4 cores | Speedup |
|---|---|---|---|
| Single package, 100 commits | 7 ms | 8 ms | 0.9× |
| Monorepo, 10 packages × 100 commits | 10 ms | 10 ms | 1.0× |
| Monorepo, 50 packages × 500 commits | 25 ms | 24 ms | 1.0× |
| Monorepo, 100 packages × 1k commits | 54 ms | 57 ms | 0.9× |
| Monorepo, 50 packages × 5k commits | 193 ms | 192 ms | 1.0× |
| Monorepo, 200 packages × 10k commits | 583 ms | 588 ms | 1.0× |
| Monorepo, dependency graph, 50 packages × 300 commits | 22 ms | 25 ms | 0.9× |
Warm cache
Every number above is measured cold. In practice a repository rarely moves between two ferrflow check calls, so FerrFlow keeps the result of its last analysis in .git/ferrflow-cache and reuses it while HEAD, the tags and the config are unchanged. This is what a second call costs on an unchanged repo. It's a real property of the tool, but it isn't a like-for-like comparison, so it stays out of the chart.
| Fixture | Cold | Cached | Speedup |
|---|---|---|---|
| Single package, 100 commits | 8 ms | 3 ms | 2.3× |
| Monorepo, 10 packages × 100 commits | 11 ms | 4 ms | 2.9× |
| Monorepo, 50 packages × 500 commits | 25 ms | 4 ms | 5.7× |
| Monorepo, 100 packages × 1k commits | 53 ms | 6 ms | 8.3× |
| Monorepo, 50 packages × 5k commits | 190 ms | 5 ms | 41.3× |
| Monorepo, 200 packages × 10k commits | 579 ms | 5 ms | 125.9× |
| Monorepo, dependency graph, 50 packages × 300 commits | 23 ms | 4 ms | 5.3× |
Install footprint
Disk size after install. release-please appears here only: its runtime can't be benched offline (it needs the GitHub API to resolve the latest release), so footprint is the fair comparison we can publish.
| Tool | npm install | Native binary |
|---|---|---|
| ferrflow | 8.0 MB | 8.0 MB |
| changesets | 5.0 MB | — |
| commit-and-tag-version | 8.0 MB | — |
| release-please | 95.0 MB | — |
| semantic-release | 69.0 MB | — |
| standard-version | 21.0 MB | — |
Why FerrFlow ships smaller numbers
- Native binary. No Node.js boot, no npm install, no plugin discovery. A
ferrflow checkon a 50-package monorepo finishes beforenode --versionwould have warmed up. - gitoxide in-process. Tag enumeration and commit walking happen via gitoxide in the same process. Competitors shell out to
gitper call; process-spawn overhead dominates on dense histories. - Single tool, not a plugin tree. semantic-release pulls a chain of analyzers, notes generators, npm publishers and changelog writers, each with its own require() cost. FerrFlow's pipeline is one Rust crate.
Numbers updating every release. See the latest release notes for the full table including baseline delta.