Benchmarks
All benchmarks run on an AMD Ryzen 9 9900X 12-Core Processor (24 cores, 121 GB RAM), averaged over 100 runs after 10 warmup iterations. Times in milliseconds (lower is better) unless noted otherwise.
Startup Latency
Section titled “Startup Latency”How fast each shell can execute shell -c 'true' and exit.
shell -c 'true' — round-trip
| shell | median (ms) | stddev (ms) | throughput (MB/s) |
|---|---|---|---|
| dash | 0.26 | 0.05 | — |
| bash | 0.43 | 0.13 | — |
| sh | 0.47 | 0.08 | — |
| lash | 0.55 | 0.07 | — |
| zsh | 0.56 | 0.05 | — |
| fish | 6.45 | 1.12 | — |
shell -c 'echo x' — round-trip
| shell | median (ms) | stddev (ms) | throughput (MB/s) |
|---|---|---|---|
| dash | 0.21 | 0.03 | — |
| sh | 0.42 | 0.08 | — |
| bash | 0.43 | 0.12 | — |
| zsh | 0.51 | 0.06 | — |
| lash | 0.57 | 0.12 | — |
| fish | 6.42 | 1.02 | — |
shell -c 'echo x | cat' — round-trip
| shell | median (ms) | stddev (ms) | throughput (MB/s) |
|---|---|---|---|
| dash | 0.61 | 0.15 | — |
| bash | 0.74 | 0.11 | — |
| lash | 0.75 | 0.14 | — |
| sh | 0.77 | 0.10 | — |
| zsh | 0.91 | 0.15 | — |
| fish | 6.13 | 0.88 | — |
Pipe Throughput
Section titled “Pipe Throughput”Raw data throughput through pipes. MB/s charts are higher-is-better.
64 MB single pipe — throughput
| shell | median (ms) | stddev (ms) | throughput (MB/s) |
|---|---|---|---|
| lash | 7.21 | 1.08 | 8878 |
| lash-turbo | 7.32 | 0.63 | 8745 |
| dash | 7.69 | 0.55 | 8325 |
| zsh | 8.46 | 0.86 | 7566 |
| bash | 8.64 | 0.68 | 7409 |
| sh | 9.05 | 0.87 | 7073 |
| fish | 15.03 | 1.61 | 4257 |
1 GB single pipe — throughput
| shell | median (ms) | stddev (ms) | throughput (MB/s) |
|---|---|---|---|
| lash-turbo | 118.12 | 22.92 | 8669 |
| bash | 138.84 | 8.94 | 7375 |
| sh | 140.05 | 10.01 | 7312 |
| zsh | 140.09 | 5.35 | 7309 |
| lash | 141.43 | 6.91 | 7240 |
| dash | 144.08 | 9.29 | 7107 |
| fish | 147.22 | 11.22 | 6955 |
64 MB through 3 cat stages
| shell | median (ms) | stddev (ms) | throughput (MB/s) |
|---|---|---|---|
| lash-turbo | 7.38 | 0.61 | 8674 |
| sh | 8.35 | 0.85 | 7664 |
| bash | 8.80 | 0.68 | 7276 |
| lash | 8.90 | 0.88 | 7193 |
| zsh | 8.95 | 0.74 | 7152 |
| dash | 9.30 | 0.59 | 6884 |
| fish | 16.08 | 1.29 | 3981 |
16 MB streamed to sink
| shell | median (ms) | stddev (ms) | throughput (MB/s) |
|---|---|---|---|
| dash | 2.30 | 0.27 | 6952 |
| lash-turbo | 2.56 | 0.09 | 6248 |
| bash | 2.67 | 0.20 | 5997 |
| sh | 2.70 | 0.40 | 5935 |
| zsh | 2.88 | 0.20 | 5565 |
| lash | 2.93 | 0.33 | 5469 |
| fish | 8.88 | 0.59 | 1802 |
echo | cat — command latency
| shell | median (ms) | stddev (ms) | throughput (MB/s) |
|---|---|---|---|
| dash | 0.46 | 0.09 | — |
| lash-turbo | 0.63 | 0.06 | — |
| lash | 0.68 | 0.09 | — |
| bash | 0.71 | 0.05 | — |
| sh | 0.71 | 0.07 | — |
| zsh | 1.00 | 0.12 | — |
| fish | 6.03 | 1.29 | — |
Pipe Chain Depth
Section titled “Pipe Chain Depth”echo through 5 cat stages
| shell | median (ms) | stddev (ms) | throughput (MB/s) |
|---|---|---|---|
| dash | 0.79 | 0.12 | — |
| lash-turbo | 0.91 | 0.33 | — |
| lash | 0.93 | 0.65 | — |
| bash | 1.07 | 0.11 | — |
| sh | 1.07 | 0.07 | — |
| zsh | 1.44 | 0.17 | — |
| fish | 7.05 | 0.76 | — |
echo through 10 cat stages
| shell | median (ms) | stddev (ms) | throughput (MB/s) |
|---|---|---|---|
| dash | 0.98 | 0.09 | — |
| lash-turbo | 1.15 | 0.14 | — |
| bash | 1.24 | 0.10 | — |
| lash | 1.27 | 0.12 | — |
| sh | 1.41 | 0.20 | — |
| zsh | 1.93 | 0.23 | — |
| fish | 8.07 | 0.88 | — |
Pipe Scaling (16 MB)
Section titled “Pipe Scaling (16 MB)”16 MB — 1 cat stage
| shell | median (ms) | stddev (ms) | throughput (MB/s) |
|---|---|---|---|
| lash-turbo | 2.61 | 0.25 | 6120 |
| dash | 2.68 | 0.47 | 5968 |
| sh | 2.92 | 2.03 | 5485 |
| bash | 2.95 | 0.21 | 5421 |
| zsh | 3.03 | 0.33 | 5274 |
| lash | 3.12 | 0.46 | 5123 |
| fish | 9.53 | 1.15 | 1679 |
16 MB — 4 cat stages
| shell | median (ms) | stddev (ms) | throughput (MB/s) |
|---|---|---|---|
| lash-turbo | 2.26 | 0.34 | 7092 |
| dash | 3.43 | 0.42 | 4665 |
| sh | 3.57 | 0.37 | 4477 |
| bash | 3.75 | 0.39 | 4267 |
| lash | 3.76 | 1.86 | 4251 |
| zsh | 4.02 | 0.42 | 3976 |
| fish | 10.54 | 0.93 | 1519 |
16 MB — 8 cat stages
| shell | median (ms) | stddev (ms) | throughput (MB/s) |
|---|---|---|---|
| lash-turbo | 2.43 | 0.34 | 6591 |
| dash | 3.59 | 0.60 | 4456 |
| bash | 5.15 | 2.56 | 3109 |
| lash | 5.26 | 1.51 | 3044 |
| zsh | 5.91 | 0.57 | 2708 |
| sh | 6.70 | 1.10 | 2388 |
| fish | 11.19 | 0.70 | 1430 |
16 MB — 16 cat stages
| shell | median (ms) | stddev (ms) | throughput (MB/s) |
|---|---|---|---|
| lash-turbo | 2.63 | 0.65 | 6082 |
| bash | 6.78 | 1.02 | 2361 |
| dash | 6.98 | 0.83 | 2291 |
| lash | 7.01 | 1.54 | 2281 |
| zsh | 8.49 | 0.87 | 1885 |
| sh | 8.95 | 1.83 | 1787 |
| fish | 14.06 | 0.55 | 1138 |
File I/O
Section titled “File I/O”16 MB write to file
| shell | median (ms) | stddev (ms) | throughput (MB/s) |
|---|---|---|---|
| sh | 2.55 | 0.21 | 6284 |
| zsh | 2.60 | 0.32 | 6147 |
| dash | 3.07 | 0.60 | 5216 |
| bash | 3.13 | 0.47 | 5113 |
| lash | 3.15 | 0.78 | 5072 |
| lash-turbo | 3.60 | 0.63 | 4443 |
| fish | 9.41 | 0.82 | 1700 |
16 MB pipe to file
| shell | median (ms) | stddev (ms) | throughput (MB/s) |
|---|---|---|---|
| lash-turbo | 3.89 | 0.72 | 4108 |
| dash | 4.48 | 0.54 | 3571 |
| bash | 4.51 | 0.55 | 3549 |
| sh | 4.54 | 0.77 | 3521 |
| lash | 4.62 | 0.53 | 3465 |
| zsh | 4.72 | 0.55 | 3392 |
| fish | 11.84 | 0.93 | 1351 |
16 MB read from file through pipe
| shell | median (ms) | stddev (ms) | throughput (MB/s) |
|---|---|---|---|
| lash-turbo | 6.93 | 0.48 | 2310 |
| sh | 7.03 | 0.66 | 2277 |
| dash | 7.19 | 0.34 | 2225 |
| lash | 7.50 | 1.24 | 2132 |
| zsh | 7.53 | 1.41 | 2125 |
| bash | 7.60 | 0.43 | 2106 |
| fish | 14.37 | 0.44 | 1113 |
16 MB to /dev/null (overhead baseline)
| shell | median (ms) | stddev (ms) | throughput (MB/s) |
|---|---|---|---|
| dash | 0.56 | 0.02 | 28546 |
| sh | 0.82 | 0.03 | 19465 |
| bash | 0.84 | 0.09 | 18957 |
| lash-turbo | 0.89 | 0.16 | 17998 |
| zsh | 0.90 | 0.13 | 17699 |
| lash | 0.93 | 0.25 | 17214 |
| fish | 6.70 | 0.63 | 2388 |
Turbo Mode
Section titled “Turbo Mode”Turbo mode rewrites common pipelines into native array operations — no fork/exec overhead. Speedups are turbo vs forked lash.
seq 1M | sort | tail — turbo 6.9x vs forked
| shell | median (ms) | stddev (ms) | throughput (MB/s) |
|---|---|---|---|
| lash-turbo | 38.83 | 1.62 | — |
| lash | 297.90 | 11.65 | — |
| fish | 300.05 | 5.77 | — |
| bash | 305.20 | 14.44 | — |
| zsh | 307.35 | 106.35 | — |
| dash | 307.95 | 16.57 | — |
| sh | 313.56 | 12.58 | — |
sort 100K lines — turbo 3.9x vs forked
| shell | median (ms) | stddev (ms) | throughput (MB/s) |
|---|---|---|---|
| lash-turbo | 6.88 | 0.28 | — |
| lash | 26.61 | 0.41 | — |
| sh | 26.83 | 1.01 | — |
| zsh | 27.76 | 1.08 | — |
| dash | 27.79 | 0.96 | — |
| bash | 29.28 | 1.50 | — |
| fish | 35.14 | 2.55 | — |
sort | head from 100K — turbo 7.7x vs forked
| shell | median (ms) | stddev (ms) | throughput (MB/s) |
|---|---|---|---|
| lash-turbo | 4.04 | 0.15 | — |
| zsh | 23.59 | 0.28 | — |
| lash | 23.61 | 0.50 | — |
| sh | 23.79 | 0.45 | — |
| bash | 23.89 | 1.36 | — |
| dash | 23.92 | 0.80 | — |
| fish | 29.95 | 1.35 | — |
sort | tail from 100K — turbo 5.2x vs forked
| shell | median (ms) | stddev (ms) | throughput (MB/s) |
|---|---|---|---|
| lash-turbo | 4.37 | 0.06 | — |
| lash | 25.51 | 4.46 | — |
| sh | 25.60 | 0.69 | — |
| bash | 25.73 | 0.76 | — |
| dash | 26.09 | 1.21 | — |
| zsh | 26.19 | 0.83 | — |
| fish | 32.43 | 1.08 | — |
grep | sort | head from 100K — turbo 2.6x vs forked
| shell | median (ms) | stddev (ms) | throughput (MB/s) |
|---|---|---|---|
| lash-turbo | 3.69 | 0.16 | — |
| dash | 9.99 | 0.16 | — |
| lash | 10.32 | 0.53 | — |
| sh | 10.33 | 0.85 | — |
| bash | 10.84 | 0.97 | — |
| zsh | 10.88 | 0.40 | — |
| fish | 17.68 | 0.76 | — |
sort 1M lines reverse numeric — turbo 7.8x vs forked
| shell | median (ms) | stddev (ms) | throughput (MB/s) |
|---|---|---|---|
| lash-turbo | 40.06 | 1.72 | — |
| bash | 304.94 | 23.31 | — |
| lash | 306.88 | 14.36 | — |
| fish | 312.86 | 15.33 | — |
| sh | 315.27 | 7.75 | — |
| dash | 316.10 | 18.98 | — |
| zsh | 320.10 | 19.97 | — |
sort+head+sort+tail from 100K — turbo 6.2x vs forked
| shell | median (ms) | stddev (ms) | throughput (MB/s) |
|---|---|---|---|
| sh | 39.11 | 0.55 | — |
| zsh | 39.32 | 0.69 | — |
| bash | 40.51 | 1.04 | — |
| lash | 40.72 | 2.52 | — |
| dash | 41.37 | 3.36 | — |
| fish | 47.16 | 1.73 | — |
| lash-turbo | 493.84 | 7.59 | — |
5-stage pipeline on 500K — turbo 4.2x vs forked
| shell | median (ms) | stddev (ms) | throughput (MB/s) |
|---|---|---|---|
| zsh | 101.49 | 1.48 | — |
| sh | 102.54 | 2.85 | — |
| dash | 107.52 | 7.59 | — |
| lash | 108.93 | 6.12 | — |
| bash | 109.20 | 4.49 | — |
| fish | 111.74 | 1.50 | — |
| lash-turbo | 3154.31 | 33.93 | — |
generate+sort+uniq+sort from 100K — turbo 6.5x vs forked
| shell | median (ms) | stddev (ms) | throughput (MB/s) |
|---|---|---|---|
| lash-turbo | 36.42 | 1.79 | — |
| lash | 193.03 | 2.73 | — |
| bash | 194.07 | 2.08 | — |
| sh | 197.03 | 8.69 | — |
| zsh | 202.54 | 6.17 | — |
| dash | 202.95 | 3.79 | — |
| fish | 214.66 | 4.80 | — |
Scripting Operations
Section titled “Scripting Operations”Common data-processing patterns across shells.
sort 1K lines — reverse numeric
| shell | median (ms) | stddev (ms) | throughput (MB/s) |
|---|---|---|---|
| lash-turbo | 0.65 | 0.12 | — |
| dash | 0.66 | 0.04 | — |
| lash | 0.96 | 0.07 | — |
| bash | 1.01 | 0.16 | — |
| sh | 1.02 | 0.11 | — |
| zsh | 1.20 | 0.10 | — |
| fish | 6.88 | 0.74 | — |
sort 10K lines — reverse numeric
| shell | median (ms) | stddev (ms) | throughput (MB/s) |
|---|---|---|---|
| lash-turbo | 1.16 | 0.09 | — |
| dash | 2.82 | 0.27 | — |
| lash | 3.00 | 1.22 | — |
| sh | 3.01 | 0.38 | — |
| bash | 3.05 | 0.09 | — |
| zsh | 3.25 | 0.12 | — |
| fish | 10.08 | 0.80 | — |
grep filter 1K lines
| shell | median (ms) | stddev (ms) | throughput (MB/s) |
|---|---|---|---|
| lash-turbo | 0.59 | 0.03 | — |
| dash | 0.62 | 0.07 | — |
| bash | 0.83 | 0.17 | — |
| lash | 0.86 | 0.54 | — |
| sh | 0.93 | 0.14 | — |
| zsh | 1.25 | 0.13 | — |
| fish | 6.86 | 0.74 | — |
awk filter 10K lines
| shell | median (ms) | stddev (ms) | throughput (MB/s) |
|---|---|---|---|
| dash | 1.81 | 0.14 | — |
| sh | 1.99 | 0.16 | — |
| lash | 2.01 | 0.22 | — |
| bash | 2.04 | 0.14 | — |
| lash-turbo | 2.16 | 0.27 | — |
| zsh | 2.33 | 0.21 | — |
| fish | 7.74 | 1.34 | — |
awk map (x2) 10K lines
| shell | median (ms) | stddev (ms) | throughput (MB/s) |
|---|---|---|---|
| dash | 2.16 | 0.11 | — |
| sh | 2.34 | 0.14 | — |
| bash | 2.37 | 0.19 | — |
| lash | 2.38 | 0.19 | — |
| lash-turbo | 2.49 | 1.03 | — |
| zsh | 2.62 | 0.16 | — |
| fish | 9.74 | 0.99 | — |
grep pattern in 10K lines
| shell | median (ms) | stddev (ms) | throughput (MB/s) |
|---|---|---|---|
| dash | 0.64 | 0.13 | — |
| lash-turbo | 0.75 | 0.07 | — |
| sh | 0.89 | 0.12 | — |
| bash | 0.98 | 0.09 | — |
| lash | 1.02 | 0.12 | — |
| zsh | 1.06 | 0.26 | — |
| fish | 6.78 | 0.47 | — |
100K lines through grep filter
| shell | median (ms) | stddev (ms) | throughput (MB/s) |
|---|---|---|---|
| dash | 2.02 | 0.41 | — |
| lash | 2.11 | 0.11 | — |
| bash | 2.24 | 0.10 | — |
| sh | 2.29 | 0.14 | — |
| zsh | 2.54 | 0.32 | — |
| lash-turbo | 3.54 | 0.75 | — |
| fish | 10.52 | 1.42 | — |
100K small lines through pipe
| shell | median (ms) | stddev (ms) | throughput (MB/s) |
|---|---|---|---|
| dash | 1.07 | 0.22 | — |
| lash-turbo | 1.22 | 0.34 | — |
| lash | 1.44 | 0.41 | — |
| bash | 1.46 | 0.77 | — |
| sh | 1.75 | 0.50 | — |
| zsh | 2.00 | 0.37 | — |
| fish | 8.95 | 1.14 | — |
1M small lines through pipe
| shell | median (ms) | stddev (ms) | throughput (MB/s) |
|---|---|---|---|
| dash | 4.28 | 0.17 | — |
| lash-turbo | 4.35 | 0.18 | — |
| sh | 4.58 | 0.23 | — |
| zsh | 4.78 | 0.20 | — |
| bash | 4.80 | 0.39 | — |
| lash | 4.92 | 0.49 | — |
| fish | 11.84 | 0.76 | — |
Process Spawn Overhead
Section titled “Process Spawn Overhead”How fast each shell can fork and exec processes.
single fork+exec (no pipe)
| shell | median (ms) | stddev (ms) | throughput (MB/s) |
|---|---|---|---|
| dash | 0.23 | 0.06 | — |
| bash | 0.47 | 0.07 | — |
| sh | 0.51 | 0.10 | — |
| zsh | 0.64 | 0.10 | — |
| lash-turbo | 0.64 | 0.09 | — |
| lash | 0.72 | 0.35 | — |
| fish | 6.65 | 0.64 | — |
2-stage no-op pipe setup
| shell | median (ms) | stddev (ms) | throughput (MB/s) |
|---|---|---|---|
| dash | 0.41 | 0.05 | — |
| sh | 0.67 | 0.11 | — |
| bash | 0.68 | 0.09 | — |
| lash-turbo | 0.71 | 0.12 | — |
| lash | 0.74 | 0.08 | — |
| zsh | 0.77 | 0.08 | — |
| fish | 6.91 | 0.70 | — |
5-stage no-op pipe setup
| shell | median (ms) | stddev (ms) | throughput (MB/s) |
|---|---|---|---|
| dash | 0.54 | 0.08 | — |
| zsh | 0.82 | 0.18 | — |
| sh | 0.84 | 0.14 | — |
| bash | 0.85 | 0.15 | — |
| lash-turbo | 0.91 | 0.17 | — |
| lash | 0.98 | 0.15 | — |
| fish | 6.81 | 0.98 | — |
10-stage no-op pipe setup
| shell | median (ms) | stddev (ms) | throughput (MB/s) |
|---|---|---|---|
| dash | 0.69 | 0.15 | — |
| bash | 1.04 | 0.09 | — |
| sh | 1.05 | 0.13 | — |
| lash-turbo | 1.10 | 0.06 | — |
| lash | 1.15 | 0.12 | — |
| zsh | 1.52 | 0.24 | — |
| fish | 6.36 | 0.70 | — |
How Turbo Mode Works
Section titled “How Turbo Mode Works”Turbo mode applies these optimizations automatically:
- Passthrough stripping — removes identity operations so they never execute
- Numeric sort key pre-computation — pre-computes keys in O(N) instead of parsing inside the comparator at O(N log N)
- Streaming
wc -l— counts newlines in the byte stream without collecting lines - C
strtodfor numeric conversion — calls C’sstrtoddirectly, avoiding D’sto!doubleexception overhead - Fused operations —
grep | head,grep | tail, andgrep | wcrun in a single pass over the data
Running Benchmarks
Section titled “Running Benchmarks”dub run :benchmarksOptions
Section titled “Options”| Flag | Description |
|---|---|
--runs N | Number of iterations per scenario |
--warmup N | Warmup iterations before measurement |
--scenario S | Run only the named scenario |
--json | Output results in JSON format |
--verbose | Print per-iteration timings |
To reproduce these numbers:
dub run :benchmarks -- --runs 100 --warmup 10 --verbose