alint benchmarks
Fast at scale, honestly measured. ~1.7 s on a
100K-file synthetic workspace bundle, ~17.5 s at 1M files; sub-second
on lighter scans (filename hygiene 0.23 s, cross-file relational
0.53 s at 100K). Real-world: a 79-rule pass over NixOS/nixpkgs
(39,101 files) completes in 273 ms
wall-clock, faster than git status on the same repo on a
cold cache.
Hardware fingerprint: linux-x86_64 · Intel Core
i7-6700HQ (4-core) · 15 GB · ext4 · rustc 1.97 · host
kbench, a dedicated quiet box (canonical since
2026-07-15). Absolute numbers are not directly comparable across
machines. See methodology for what does
and doesn't transfer, and /benchmarks-1
for the retired 3900X series (v0.5.6–v0.13.0, with the v0.9 engine
cliffs).
What we publish, and how often
- 13 macro scenarios (S1–S13) driven by hyperfine,
run at 4 sizes (1k / 10k / 100k / 1M files) in two
modes (
fullall-files scan andchangedvia--changed). That's 104 (scenario, mode, size) cells per release. S10 added in v0.9.9 (out-of-per-file-dispatchscope_filter:); S11–S13 added in v0.10 to cover the new cross-file / per-file / single-shot dispatch classes. - 12 micro benches (criterion) covering pure-CPU primitives: glob compile, regex content scans, engine fan-out, walker, formatters. Floor-tested vs an earlier same-host baseline on every PR; anything >10 % slower fails CI.
- Per release. Numbers regenerate via the
bench-record.ymlworkflow on a self-hosted runner with a known hardware fingerprint, then land indocs/benchmarks/HISTORY.md(the canonical source of truth this page summarises).
Real-world: NixOS/nixpkgs
The largest non-trivial OSS monorepo on GitHub, used as a real-world
stress beyond the synthetic 100K bench. The 79-rule pass includes a
for_each_dir over the 20,678-directory
pkgs/by-name/*/*/ tree, exactly the cross-file dispatch
shape the v0.9.5 + v0.9.6 + v0.9.8 engine work was designed to make
linear.
| Metric | NixOS/nixpkgs |
|---|---|
| Files in tree | 39,101 (sparse-cloned) |
pkgs/by-name/*/*/ directories iterated | 20,678 |
| alint config rule count | 79 |
| Wall-clock for full check pass | 273 ms |
A real-world complement to the synthetic matrix, measured on the 3900X reference desktop (the case study predates the kbench re-baseline), not a like-for-like kbench cell.
Synthetic: per-release trajectory
1M-file headline cells across the most-stressed scenarios, on the
current kbench series. S3 is the realistic-monorepo anchor; S6 is the
per-file content fan-out; S7 is the cross-file relational path; S9 is
the nested-polyglot scenario the scope_filter: primitive
exists for. The trajectory is flat by design; the dramatic v0.9
engine cliffs are on the retired 3900X
series.
| Version | Date | 1M S3 full | 1M S6 full | 1M S7 full | 1M S9 full |
|---|---|---|---|---|---|
| v0.13.0 | 2026-06-17 | 17.46 s | 16.65 s | 16.09 s | 11.39 s |
| v0.12.0 | 2026-06-07 | 17.59 s | 16.74 s | 16.09 s | 11.43 s |
| v0.11.0 | 2026-05-28 | 17.52 s | 16.66 s | 16.26 s | 11.44 s |
| v0.10.2 | 2026-05-21 | 17.34 s | 16.59 s | 16.02 s | 11.37 s |
| v0.10.1 | 2026-05-20 | 17.43 s | 16.59 s | 16.05 s | 11.41 s |
| v0.10.0 | 2026-05-20 | 17.65 s | 16.70 s | 16.19 s | 11.46 s |
This kbench series is deliberately flat: from v0.10 through the latest release, performance holds steady while the rule surface grew. The dramatic engine cliffs (S3 731.9 s → 12.59 s at v0.9.5; S7 614.4 s → 15.41 s at v0.9.8), each with an investigation write-up, are on the retired 3900X series. See investigations/ for the diagnostic write-ups.
What each scenario stresses
| Scenario | Shape | Catches regressions in… |
|---|---|---|
| S1 Filename hygiene | 8 filename-only rules | Walker + scope-match |
| S2 Existence + content | 8 existence + content rules | Per-file content fan-out |
| S3 Workspace bundle | extends: oss-baseline + rust + monorepo + cargo-workspace (32 rules)
| Realistic monorepo workload |
| S4 Agent-era hygiene | 5 rules from agent-hygiene@v1 | Agent-era rule shapes |
| S5 Fix-pass content edits | 4 content-edit rules under --fix | Fix-pipeline regressions |
| S6 Per-file content fan-out | 13 content rules over **/*.rs | Per-file inner-loop |
| S7 Cross-file relational | 6 cross-file kinds (pair,
unique_by, for_each_dir, …)
| Cross-file dispatch cliff |
| S8 Git overlay |
S3 reshape + git_no_denied_paths +
git_tracked_only | Git-aware dispatch |
| S9 Nested polyglot | extends: rust + node + python (26 rules) over
polyglot tree with scope_filter: | Polyglot scope-filter dispatch |
Methodology, short version
Two layers. criterion for pure-CPU micro-benchmarks
(stable, cross-platform, run on every PR).
hyperfine driven by xtask bench-scale
for end-to-end CLI wall-time (cross-platform, reproducible, honest
about variance, run before each release tag).
Three deliberate methodology choices:
- Hyperfine, not a custom Rust harness. Hyperfine measures wall-time of an external command from outside the process: exactly the cost shape a CLI user pays, including process startup, dynamic linker overhead, stdio buffering, format selection. A Rust-internal harness would skip those and overstate alint's speed.
- Deterministic synthetic monorepo, not a real-world repo
for cross-version comparison. Synthetic trees are byte-identical
across machines given the same seed (
0xA11E47): 1k = 1,001 files exactly, 1M = 1,000,001 files exactly, no tree-size drift contaminating cross-version comparisons. The nixpkgs data point above is the non-synthetic complement. - Not CodSpeed / iai-callgrind. Both are Valgrind-
based; alint's hot path is syscall-heavy (the
ignore-crate walk), and Valgrind's instruction counts drift whenever the CI runner's glibc or kernel updates, exactly the part of alint we most want stable numbers for.
Honest comparisons
Mostly we don't have apples-to-apples public benches, because the other tools haven't published any.
- vs Repolinter. No public benches exist; Repolinter was archived 2026-02. Architectural shape (Node startup ~100 ms + per-rule JS execution) suggests 1–2 orders of magnitude slower at 100K files, but that's an estimate, not a measurement.
- vs ls-lint. No public benches at scale. ls-lint is a Go binary doing filename + directory matching only: narrower scope, likely faster than alint at S1's specific shape. If filename conventions are the only thing you care about, ls-lint will likely be faster.
- vs Megalinter. Shape-mismatch comparison. Megalinter is a Docker orchestrator, not a linter. Its wall-time is dominated by container startup + per-tool execution, not by any single tool's hot path. Use Megalinter alongside alint, not instead of.
- vs custom shell scripts. Each repo's
verify-*.shdirectory is bespoke; the wall-time comparison doesn't generalise. The kubernetes case study replaced 17 of 50 verify scripts with one alint config; the win shows up in CI wall-time variance more than in raw speed.
See /compare/ for the full feature-matrix comparison.
Every published number is reproducible end-to-end on your own hardware.