Let the numbers speak▊
Every number carries an artifact or PR anchor (noted row by row); claims we haven't measured don't get printed on this page.
Startup · latency · size
| Metric | niubash / rubash | Baseline | Ratio | Source |
|---|---|---|---|---|
| git startup (git --version) | 13.7 ms avg / 11.5 ms min · git status 22.6 ms | Git Bash 45–103 ms (same rig in niu-git#12: mingw64\bin 51.3 ms · cmd\git.exe 45.3 ms) | 3–7x faster | niu-git#12 (merged 2026-10-16) · niubash#374 |
| One-shot niu -c true | 26.3 ms | Git Bash 71 ms | 2.7x faster | GITBASH-KILLER card 6 |
| Prompt git segment | reads .git/HEAD ≤29 ms | spawns git per prompt, 113–362 ms | 4–12x faster | GITBASH-KILLER card 3 (real repo, min-of-20) |
| shell startup (5 measured runs) | rubash 27–48 ms | Git Bash 60–93 ms · WSL 202–278 ms | 2–3x faster | data/bench/startup.tsv |
| Install size | niu ≈16 MB · zero DLLs | Git Bash 440 MB + 20 MB msys-2.0.dll | 27x lighter | GITBASH-KILLER card 4 (measured on a real install) |
| WinuxCmd | 3.79 MB carrying 176 commands / 1,924 options · ~19 ms cold start | — | — | WinuxCmd README:32,:74 (PGO: sort −18%, nl −17%) |
Same Windows machine: 8/8 faster than Git Bash
2026-10-18 · rubash master 8ab35e89 (incl. #807/#808) · method: Interleaved rounds (rubash → Git Bash → WSL per round), 1 warm-up round discarded, min-of-3, 120 s timeout, identical timing across all three (inner window date +%s%N).
Source: data/bench/results.tsv + data/bench/run_bench.sh
| Workload | rubash vs Git Bash | rubash | Git Bash |
|---|---|---|---|
| Background wait sleep & wait ×750 | 1,696 ms | 1,967 ms | |
| Background wait ×1500 | 3,366 ms | 3,980 ms | |
| Background wait ×300 | 694 ms | 814 ms | |
| Background wait ×100 | 263 ms | 298 ms | |
| Background subshell ( ) ×750 | 1,643 ms | 2,192 ms | |
| Pure spawn ×750 | 1,696 ms | 2,119 ms | |
| Foreground external command ×750 | 37 ms | 41 ms | |
| Built-in loop ×750 | 32 ms | 40 ms |
WSL is a cross-OS reference and meaningless against native Windows, so it is no longer shown in the main table; on native Linux (Android builds included), rubash is also significantly faster than GNU bash on fork/subshell workloads — a dedicated suite will be listed once its artifacts land.
Android baseline (Termux aarch64): measured, honest, post-fix re-run pending niu vs GNU bash 5.3.20 (aarch64) post-fix re-run pending
2026-10-14 · Android tablet · aarch64 · 6 cores · Termux · niu 1.4.0 Android build (embeds rubash e785c3c8f610) vs GNU bash 5.3.20-1 aarch64 (Termux package).
Method: same script, interleaved (bash→niu→bash→niu), best-of-3; startup is a median of 20–30 warm runs (wait4 rusage).
Source: niubash#352 (full matrix & method) · niubash#349 (the original ~2.1x arithmetic/function datapoint)
| Workload (v1.4.0 snapshot) | GNU bash | niu | Verdict |
|---|---|---|---|
| startup niu -c true (median of 30) | 8.3 ms | 21.1 ms | 2.5x slower (+14 ms is the wrapper gap) |
| arithmetic for loop ×200k | 0.572 s | 1.353 s | 2.37x slower |
| string expansion ×30k (//, ^^, #, %) | 0.325 s | 1.198 s | 3.69x slower (${x^^} alone up to 7.3x) |
| pure assignment t=$S ×30k | 0.086 s | 0.234 s | 2.72x slower |
| peak RSS (VmHWM) | 4.6 MB | 10.2 MB | 2.2x |
| fib recursion via $( ) ×3000 | 855.9 s | 164.8 s | 5.2x faster |
| subshell/pipeline ×3000 | 68.0 s | 37.9 s | 1.8x faster |
| real script mkdir/write/read-back ×500 | 9.77 s | 4.55 s | 2.1x faster |
- The engine-side root causes are fixed and closed (rubash#607-609 → #665/#671/#679, #666, #672/#678); the wrapper’s one-shot startup gap is closed too (niubash#351). No post-fix re-measure on Android hardware exists yet — #352 stays open for that re-run and the Android leg still ships build-verified only. The table is a v1.4.0 snapshot, not the current version.
- wrapper startup gap (Android rig): after the fix niu -c true 15 ms vs bash 13 ms, zero tput spawns (
niubash#351 (closed)) - scalar assignment: net gap vs GNU down to ~1.06x (2.72x on v1.4.0) (
rubash#609 → #672/#678 (all merged)) - arithmetic for / loops: `:` loop 2.03x → 1.77x (three fixes merged) (
rubash#607 → #665/#671/#679 (all merged)) - parameter expansion: ${s^} ~6x fix landed, differential battery 33/33 (
rubash#608 → #666 (merged) · rubash docs/performance.md ledger)
Fork / subshell deep dive rubash engine vs GNU bash 5.3.20 placeholder · pending TSV
2026-10-18 · rubash HEAD 141b460 / niu 1.4.0 · method: Sequential interleaving, min-of-N, per-item timeout guard. Placeholder: figures transcribed from the 2026-10-18 re-run output; promoted once the recheck_fork_20261018-*.tsv artifacts land in data/bench/ (this note is removed after they verify).
| Workload | bash → rubash | GNU bash | rubash | niu | Ratio |
|---|---|---|---|---|---|
| $( ) command substitution ×3000 | 4,382 ms | 90 ms | 119 ms | 48.7x | |
| Backtick substitution ×3000 | 4,903 ms | 79 ms | 107 ms | 62x | |
| Subshell ( : ) ×5000 | 5,799 ms | 358 ms | 382 ms | 16.2x | |
| Recursive fib $() ×100 | 37,300 ms | 2,866 ms | 3,019 ms | 13.0x | |
| External process loop /bin/true ×2000 | 16,229 ms | 16,404 ms | 16,478 ms | 1.01x | |
| Background sleep & wait ×750 | 928 ms | 2,293 ms | 2,423 ms | 2.47x |
Workloads that also include inline-coreutils gains (listed separately, not counted toward the fork record): Pipeline echo | cat ×2000 190x (17,347→91 ms); Real script mkdir/wc ×250 2.14x (4,123→1,928 ms).
- Pure fork/subshell workloads (command substitution, backticks, subshells, recursion) run 13–62x faster than GNU bash — the direct payoff of the zero-fork, three-tier execution model
- External process loop ≈1.01x, on par: the real exec cost is still there, no fake speedups (shown as-is)
- The pipeline and real-script rows include inline-coreutils gains (in-process execution spawns no children); they are listed separately and not counted toward the fork record
- niu (the product shell) and rubash (the engine) track each other closely in ratio
Engineering numbers before → after, each with a PR anchor
| Item | Number | Source |
|---|---|---|
| One-shot execution optimization (xgr series of six) | niu -c true 105.6 ms → 19.8 ms | niubash PR #267–#272 |
| Multi-line paste | accidental auto-executions 186 → 0; 257-line payload 300 s timeout → 4.6 s | PR #252/#288 · paste_multiline_bench.py |
| nvm.sh re-lex checkpoint | gap vs GNU 720x → 8x, re-lexed bytes −40% | rubash PR #486 |
| Array element writes O(n²)→O(1) amortized | K=5000 single write vs GNU: associative 445x → 9.9x, indexed 736x → 3.8x (the old build was 45x / 177x vs itself); also fixes the O(n²) tokenizer (10,001-segment parse 563→17.8 ms) | rubash#618/#616 (ledger: docs/performance.md) · earlier #650/#652 |
| Single spawn cost | rubash 1.183 ms vs GNU 1.121 ms — on par (the real exec floor) | data/bench/unix-spawn-cost-20261012.md (min-of-3) |
| Windows hidden-console allocation | console-less sessions used to pay ~22 ms per child process → now one allocation per command tree (a windowless console pre-allocated on Win11 24H2+, or adopted from the first CREATE_NO_WINDOW child on older builds); pinned by 14 regression tests | rubash#913 (merged 2026-10-20) · design in niubash#462 |
| $(tput …) terminal queries answered in-process | the 64-call tput ticket probe fell 1165 ms → 47 ms (~25x, zero spawns, byte-equal to GNU ncurses); the winuxcmd-side twin removed ~17 ms/call of i18n overhead → ~0.4 ms (3,208-row replay byte-identical) | rubash#884 · WinuxCmd#1269(均 merged) |
| [[ ]] direct numeric leaf + decimal fast path | 50k [[ 1 -eq 2 ]] 1.41x → 1.10x vs GNU | rubash#966(merged 2026-10-21) |
| Multi-word literal command fast path | [ 1 -eq 2 ] ×20k ≈177 → 155 ms | rubash#967(merged 2026-10-21) |
| Whitelist-skip of the procsub metadata scan | 1.56x → 1.39x (~11%) | rubash#974(merged 2026-10-21) |
| Decimal-subscript array write fast lane | interleaved 8k 3.65x → 3.16x; also fixes a real double side-effect in a[$((i++))]=v | rubash#959(merged 2026-10-21) |
| case: render $BASH_COMMAND head-only | 20-arm ×10k −10%, plus a real divergence fix | rubash#958(merged 2026-10-21) |
| export/unset fast-lane family | single-probe mirror + bulk unmark; the residual ~100x face stays open (shown as-is) | rubash#917/#918/#920 + #943 正确性返工(均 merged)· #916 开放 |
| Silent-builtin short-circuit | returns directly on empty output with no redirects; shift ~1.5–4% (still ~2x bash — the bottleneck is elsewhere); export/unset counts unchanged | rubash#984 (merged 2026-10-22) · landed in 0aeffe99 |
| Associative-subscript fast lane | plain-$name subscript fast lane, interleaved 8k 3.5x → 2.6x (linear, ~3.1x asymptote) | rubash#985 (merged 2026-10-22) · landed in 0aeffe99 |
| export/unset IFS-marking skip | quoteless words skip identity IFS marking, export/unset 2k 2.14x → 1.97x (~3–4%) | rubash#986 (merged 2026-10-22) · landed in 0aeffe99 |
| Compound-literal linearization | subscript-span scan O(n²) → linear, 8k fill ~850ms → ~200ms, 20k ~5.1s → 640ms (~8x) | rubash#987 (merged 2026-10-22) · landed in 0aeffe99 |
| [ $VAR -op N ] integer fast-expand (pending) | loop-while 30k ~2.4x → ~1.8x vs bash; merge pending, not counted in the main table yet | rubash#990 (OPEN, pending) |
Methodology & sources
- Environment: Intel Xeon w5-3435 · Windows 10 19045 · 同机 WSL2 Ubuntu(GNU Bash 5.3.0 对照)· Git Bash 5.3.0 (msys);Android 节另有 Termux aarch64 口径; raw artifacts checked into
data/bench/(15 files, incl. BENCH-README.md with the environment and method notes) - Discipline: nothing unmeasured gets written down; ties and losses are listed too (single-spawn parity = the real exec floor; the cross-OS WSL reference has been removed from the main table)
- Source of truth: FACTS.json (portal-dump branch, 2026-10-19) — where the README disagrees, FACTS wins