Skip to content

Benchmarks

These results measure one synthetic workload with overlapping linter file patterns. They illustrate how orchestration affects this setup; they are not a prediction for every repository or a survey of every tool’s available configuration.

The recorded run was generated on 2026-03-23. No new benchmark run is implied by this page’s last-updated date.

Recorded results

Mean wall time, in seconds. Lower is faster.

ToolAll files (6158)Staged changes (50)
hk3.15 s0.42 s
lefthook20.83 s0.72 s
pre-commit10.60 s0.81 s
prek10.52 s0.74 s

Recorded mean runtimes for all-files and staged-change scenarios; values are in the table above

Download the recorded data, including standard deviations, minimums, and maximums.

Workload

The generator defaults to roughly 6,000 files: 4,000 Python, 500 JavaScript/TypeScript, 500 JSON, 500 shell, 250 YAML, 200 CSS, and 200 Markdown files, plus project configuration.

Ten configured steps include ESLint, Prettier, Black, Ruff linting, Ruff formatting, jq, yq, shfmt, trailing whitespace, and final newlines. The whitespace steps overlap with the language-specific steps.

The committed runner:

  • Invokes hk’s pre-commit hook in fix mode, with stashing disabled through HK_STASH=false.
  • Configures lefthook with sequential execution to avoid concurrent writes from overlapping formatters in this workload.
  • Runs pre-commit and prek using the provided hook definitions.
  • Resets the fixture between runs, primes hk’s configuration cache, and uses Hyperfine warmups and repeated measurements.

The runner and tool configurations define the comparison. These choices matter as much as the timing values.

Limitations

This workload favors concurrent work across languages while also exercising overlapping formatters. A small project, a single linter, or tools that already parallelize internally may see different results.

Stashing is disabled in the runner, so the results do not measure partial-commit restoration. Hyperfine is configured to tolerate nonzero exits from lint commands; timings alone do not establish equivalent fixes or successful checks.

The recorded JSON does not include machine specifications or exact tool versions. Treat it as a historical example and rerun the workload with those details recorded before using the numbers for a tool-selection decision. Current scripts may also differ from the ones used for the recorded result.

Reproduce

Use a disposable directory. The benchmark runner resets its fixture repository and overwrites generated results.

Install hk, hyperfine, lefthook, pre-commit, prek, prettier, eslint, black, ruff, shfmt, jq, yq, and uv, and record their versions. The shell scripts expect a Unix-like environment and compatible command-line utilities.

From the repository root:

sh
benchmark/generate-project.sh /tmp/hk-bench
benchmark/run.sh /tmp/hk-bench

To change the workload or number of repetitions:

sh
NUM_JS=500 NUM_PY=500 benchmark/generate-project.sh /tmp/hk-bench
RUNS=20 WARMUP=3 benchmark/run.sh /tmp/hk-bench

Results are written to benchmark/results/; the runner also updates docs/public/benchmark.png and docs/public/benchmark-data.json.

For your own project, start with hk timing reports. See Why hk? for the execution model.

MIT LicenseCopyright © 2026jdx.dev