TL;DR:
- Hyperfine runs a command multiple times and gives you statistically meaningful results — mean, standard deviation, min/max, and outlier detection — rather than a single timing snapshot
- Warmup runs eliminate OS caching effects that skew naive
timemeasurements - Parameter sweeps let you benchmark across a range of inputs in a single invocation
Here’s a command every developer has run at least once: time ./my-script.sh. You get a number, you tweak the script, you run time again, and you compare two numbers with no idea whether the difference is real or just scheduling noise. Hyperfine is the tool that fixes this, and once you’ve used it properly you’ll have a hard time going back to time.
What Hyperfine Is
Hyperfine (github.com/sharkdp/hyperfine) is a command-line benchmarking tool written in Rust by David Peter, the same developer behind tools like bat and fd. It wraps any shell command or binary, runs it many times, and produces a proper statistical summary: mean execution time, standard deviation, min and max, and a confidence interval. It also detects outliers and warns you if results are suspiciously variable.
Installing it is straightforward on most platforms:
# macOS
brew install hyperfine
# Debian/Ubuntu
apt install hyperfine
# Cargo
cargo install hyperfine
Basic Usage
The simplest invocation looks like this:
hyperfine 'grep -r "TODO" src/'
Hyperfine will run the command ten times by default, print a progress bar while it’s working, and then output a table showing the timing statistics. The default run count is usually enough for commands that take over 100ms; for very fast commands (under a millisecond), you’ll want to increase it with --runs.
hyperfine --runs 50 'sort big-file.txt > /dev/null'
Warmup Runs
This is where Hyperfine earns its keep over bare time. The first run of any command that reads files will be slower than subsequent runs because the OS hasn’t cached the data yet. If you time a command once, you’re timing the cold-cache case. If you time it twice, you’re timing the warm-cache case. Neither tells you much on its own.
Hyperfine’s --warmup flag runs the command N times before recording begins:
hyperfine --warmup 3 'cat large-file.txt | wc -l'
This eliminates caching effects from the measurement, giving you results that reflect real repeated-execution performance rather than first-run overhead. For commands where cold-cache performance is what you actually care about — a build tool run on a cold CI runner, say — you can use --prepare to clear caches between runs:
hyperfine --prepare 'sync; echo 3 > /proc/sys/vm/drop_caches' 'cat large-file.txt | wc -l'
Comparing Multiple Commands
Benchmarking one command in isolation is occasionally useful, but the real value is comparison. Hyperfine accepts multiple commands and benchmarks them side by side:
hyperfine \
'fd --extension py .' \
'find . -name "*.py"'
After running both commands the requisite number of times, Hyperfine prints a comparison table showing which is faster and by how much, with confidence intervals. It will also tell you if the difference is statistically significant or within the noise floor.
For build tool comparisons, parser benchmarks, or anything where you’re evaluating implementation alternatives, this is the workflow you want.
Parameter Sweeps
Parameter sweeps are a feature that doesn’t have a good equivalent in any simple wrapper around time. The --parameter-scan flag runs a command across a range of numeric inputs:
hyperfine --parameter-scan threads 1 8 \
'my-tool --threads {threads} input.dat'
This benchmarks my-tool with one thread, then two, then three, up to eight — automatically. The {threads} placeholder gets substituted with each value. The output table shows you exactly where the performance curve flattens, which is usually more useful than knowing the peak throughput alone.
You can also use --parameter-list to sweep over discrete string values rather than a numeric range:
hyperfine --parameter-list format 'json,csv,msgpack' \
'my-serialiser --format {format} data.bin > /dev/null'
Exporting Results
For anything serious — regression tracking, documenting performance characteristics, sharing results with a team — Hyperfine can export its data:
hyperfine --export-json results.json \
--export-markdown results.md \
'cargo build --release'
The JSON export is machine-readable and easy to diff between runs or pull into a script. The Markdown export produces a table you can drop straight into a PR description or documentation page. There’s also --export-csv for spreadsheet workflows.
The JSON structure includes all timing data per run, not just aggregates, so you can do your own statistical analysis if the defaults aren’t quite what you need.
Shell and Environment Considerations
By default, Hyperfine runs commands through the shell (sh -c), which adds a small but consistent overhead. For commands where this matters — very fast operations in the microsecond range — use --shell none to exec the command directly:
hyperfine --shell none --runs 1000 'ls /tmp'
You can also control environment variables and working directory, which matters for benchmarks where the environment affects behaviour:
hyperfine --env RUST_LOG=warn 'cargo test 2>&1 | tail -5'
When to Reach for Hyperfine
The obvious use case is comparing two implementations of something: two sort algorithms, two database query approaches, two build configurations. But Hyperfine is also useful for establishing a performance baseline before refactoring — run it, commit the results to your repo, and re-run it after the refactor to confirm you haven’t regressed.
It’s also good for the quieter diagnostic question: why does this command take so long? Running it with varying inputs via --parameter-scan, or with and without specific flags, often makes the bottleneck obvious faster than reaching for a full profiler.
The one case where Hyperfine doesn’t help much is profiling what’s happening inside a command — it measures wall-clock time, not where that time is spent. For that you still want perf, flamegraph, or language-specific profilers. But for the before/after comparison question, which is most of what benchmarking is actually used for, Hyperfine is the right tool.