Timing a script with time tells you how long one run took. That number lies to you. Disk caches, CPU frequency scaling, and background processes all skew a single measurement. The hyperfine command fixes this by running your program many times, throwing out the noise, and giving you a mean with a standard deviation. It comes from David Peter (sharkdp), the developer behind bat and fd, and it has become my default answer whenever someone asks “which of these two commands is faster?”
What the hyperfine Command Does Differently
Run time ls five times in a row and you get five different numbers. Which one do you trust? None of them, really. The first run pays for cold caches. Later runs benefit from warm ones.
The hyperfine command runs your benchmark at least 10 times by default, calculates mean, min, max, and standard deviation, then warns you when outliers suggest interference from other programs. Statistics instead of guesswork. That’s the whole pitch.
Installing hyperfine on Linux
Debian and Ubuntu ship it in the standard repos:
sudo apt install hyperfine
On Fedora it’s sudo dnf install hyperfine, on Arch sudo pacman -S hyperfine. You can also grab a static binary from the hyperfine GitHub repository or install through cargo with cargo install hyperfine if you have a Rust toolchain around. The binary is small and has no runtime dependencies.
1. Your First Benchmark with the hyperfine Command
Pass the command you want to measure as a quoted string:
hyperfine 'grep -r "TODO" src/'
The output looks like this:
Benchmark 1: grep -r "TODO" src/
Time (mean ± σ): 68.3 ms ± 2.1 ms [User: 41.2 ms, System: 26.8 ms]
Range (min … max): 65.1 ms … 73.9 ms 41 runs
Forty-one runs, a mean, a standard deviation, and the spread. Compare that to eyeballing three time invocations.
2. Comparing Two Tools Head to Head
This is where hyperfine earns its keep. Give it multiple commands and it benchmarks each, then prints a relative comparison:
hyperfine 'grep -r "TODO" src/' 'rg "TODO" src/'
Summary
rg "TODO" src/ ran
4.82 ± 0.31 times faster than grep -r "TODO" src/
No spreadsheet, no mental math. I used exactly this when writing my ripgrep article to back up the speed claims with real numbers from my own server.
3. Warmup Runs for Cached Workloads
If your program reads files, the first run hits the disk and every run after hits the page cache. Mixing those measurements produces garbage. When you want to measure the warm case, tell hyperfine to do a few throwaway runs first:
hyperfine --warmup 3 'bat --paging=never large.log'
Three unmeasured runs prime the cache, then the real measurement starts. For most everyday comparisons this is the flag I reach for first.
4. Cold-Cache Testing with the hyperfine Command
Sometimes you want the opposite: the cold-start number. The hyperfine command supports a --prepare option that runs a setup command before every single timed run:
hyperfine --prepare 'sync; echo 3 | sudo tee /proc/sys/vm/drop_caches' 'fd -e conf . /etc'
Dropping the kernel caches before each run means every measurement starts from the same cold state. The prepare command itself is never included in the timing. I use this pattern to test how a service behaves right after boot, not after an hour of cache warming.
5. Parameterized Benchmarks Across Values
Want to know how compression level affects gzip speed? Instead of writing a loop, use a parameter scan:
hyperfine --prepare 'cp backup.sql test.sql' -P level 1 9 'gzip -{level} test.sql'
hyperfine substitutes {level} with every integer from 1 to 9 and benchmarks each variant separately. There’s also -L for a comma-separated list of arbitrary values, which is how you compare thread counts, buffer sizes, or entirely different input files in one command.
6. Exporting hyperfine Command Results
Terminal output is fine for quick checks, but for documentation you want something reusable. The hyperfine command exports to Markdown, JSON, CSV, and AsciiDoc:
hyperfine --export-markdown results.md --export-json results.json 'rg "TODO"' 'grep -r "TODO" .'
The Markdown export produces a ready-made comparison table you can paste straight into a README or blog post. The JSON export contains every individual timing, which matters when you want to plot distributions. The project ships plotting scripts for exactly that in the scripts directory of the repo.
7. Reading the Outlier Warnings
Every so often you’ll see this line under a result:
Warning: Statistical outliers were detected. Consider re-running this
benchmark on a quiet system without any interferences from other programs.
Take it seriously. A browser with forty tabs, a cron job, or a package update in the background will pollute your numbers. On my benchmarking runs I close everything nonessential and check btop first to confirm the machine is idle. If the warning persists, raise the run count with --runs 50 so the statistics can absorb the noise.
When Not to Use the hyperfine Command
hyperfine measures whole processes. That includes process startup, dynamic linking, and shell spawn overhead. For commands finishing in under a millisecond or two, that overhead dominates and your comparison measures the loader, not your code. hyperfine subtracts an estimate of the shell overhead automatically, and --shell=none removes the shell entirely, but microbenchmarks of individual functions still belong in a proper framework like Criterion for Rust or pytest-benchmark for Python.
Long-running servers are the other poor fit. hyperfine wants a command that starts, works, and exits. Measuring request latency on a running daemon is a job for load-testing tools, not for a process timer.
Where It Fits in My Workflow
Three situations keep bringing me back to the hyperfine command. Settling “tool A vs tool B” arguments with actual numbers. Checking whether an optimization in a script did anything measurable. And producing honest benchmark tables for articles like this one, since a Markdown export is one flag away.
The full option list lives in the project README. Install it, benchmark something you always assumed was fast, and be prepared for a surprise or two.