Skip to content

try, compiled: Ruby CLI to a 705KB native binary - #130

Merged
tobi merged 1 commit into
mainfrom
spinel-aot
Aug 13, 2026
Merged

try, compiled: Ruby CLI to a 705KB native binary#130
tobi merged 1 commit into
mainfrom
spinel-aot

Conversation

@tobi

@tobi tobi commented Aug 13, 2026

Copy link
Copy Markdown
Owner

Ruby try compiled all the way to a 705 KB native binary with Spinel (Matz's AOT Ruby→C compiler). Same TUI, same 399 specs as MRI, startup in under a millisecond instead of ~70 ms.

--help is 73× faster. A first TUI frame is ~50–58×. The 1.0 MiB unstripped build shrinks to 705 KB with -Os + strip.

No patches to Spinel or CRuby. ruby try.rb and the gem keep working. make native emits dist/try.c and links dist/try.

Watch it

Same session on MRI and on the native binary: type spin, cursor into the word, insert xsxpin, Ctrl-A/E, Backspace, Ctrl-U, filter tui, rename, Esc.

MRI (ruby try.rb)

try MRI

Native (dist/try)

try native

Playable: MRI asciinema · native asciinema

Numbers

hyperfine 1.19.0, --shell=none -w 3 -r 20, 2026-08-13 16:04 EDT. Ruby 3.3.8 vs Spinel 79b1aba8bdef (PR 3906 so IO#tty? / #winsize work). Native won every case.

Case MRI native Speedup
--help 69.4 ± 4.8 ms 0.956 ± 0.096 ms 72.6×
--version 70.8 ± 6.3 ms 0.959 ± 0.125 ms 73.8×
no args 68.7 ± 4.3 ms 0.982 ± 0.068 ms 69.9×
init 96.5 ± 9.4 ms 21.2 ± 1.3 ms 4.5×
TUI empty 74.0 ± 8.4 ms 1.28 ± 0.14 ms 57.8×
TUI 5 dirs 72.0 ± 6.0 ms 1.38 ± 0.08 ms 52.1×
TUI --and-keys=beta 72.4 ± 5.9 ms 2.70 ± 0.35 ms 26.8×
TUI exec beta 74.6 ± 8.1 ms 1.49 ± 0.16 ms 50.2×
clone script 70.3 ± 6.2 ms 0.959 ± 0.103 ms 73.3×
worktree script 72.9 ± 8.6 ms 1.01 ± 0.09 ms 71.9×
scan 1000 dirs 80.3 ± 7.3 ms 8.57 ± 0.69 ms 9.4×
filter 1000 dirs 93.9 ± 5.5 ms 40.9 ± 4.8 ms 2.3×

init is the outlier because native still forks ps to detect fish vs bash (~20 ms). Filtering 1000 dirs is whole-process (scan + four keystrokes + re-renders), not a microbench of the matcher. MRI never wins: interpreter boot is the MRI cost.

make native SPINEL=/path/to/spinel
./dist/try --help
eval "$(./dist/try init)"
make native-test

399/399 specs on both ruby try.rb and dist/try. Stack: this PR, then #131 (strip / -Os), then #133 (lint + MRI vs native compare).

Why the Ruby had to change

Spinel compiles a closed world. try is a small stdlib CLI, but it used a few things AOT does not have yet (FileUtils, IO#raw, method_missing, Integer#times, …). Those became MRI-compatible shims in TryCompat so the same source still runs on Ruby.

Full gap list is in the first version of this description and in the diff. Remaining: no live SIGWINCH on native, raw mode is stty, non-tty winsize is [0,0].

@tobi

tobi commented Aug 13, 2026

Copy link
Copy Markdown
Owner Author

Benchmark (hyperfine, MRI vs native)

Moved here from the TUI PR so this stack stays Spinel-only. Measured 2026-08-13 16:04 EDT. hyperfine 1.19.0, --shell=none -w 3 -r 20. Ruby 3.3.8, Spinel 79b1aba8bdef (PR 3906). Native at measurement was ~1.0 MiB unstripped; after -Os+strip (#131) dist/try is 705 KB. Native won every case.

Case MRI native Speedup
--help 69.4 ± 4.8 ms 0.956 ± 0.096 ms 72.6×
--version 70.8 ± 6.3 ms 0.959 ± 0.125 ms 73.8×
no args (exit 2) 68.7 ± 4.3 ms 0.982 ± 0.068 ms 69.9×
init 96.5 ± 9.4 ms 21.2 ± 1.3 ms 4.5×
TUI empty 74.0 ± 8.4 ms 1.28 ± 0.14 ms 57.8×
TUI 5 dirs 72.0 ± 6.0 ms 1.38 ± 0.08 ms 52.1×
TUI --and-keys=beta 72.4 ± 5.9 ms 2.70 ± 0.35 ms 26.8×
TUI exec beta 74.6 ± 8.1 ms 1.49 ± 0.16 ms 50.2×
clone script 70.3 ± 6.2 ms 0.959 ± 0.103 ms 73.3×
worktree script 72.9 ± 8.6 ms 1.01 ± 0.09 ms 71.9×
scan 1000 dirs 80.3 ± 7.3 ms 8.57 ± 0.69 ms 9.4×
filter 1000 dirs 93.9 ± 5.5 ms 40.9 ± 4.8 ms 2.3×

init still forks ps for fish vs bash (~20 ms on native).

TUI demos (MRI + native) are still on branch tui-input-field:

@tobi
tobi merged commit be56682 into main Aug 13, 2026
1 check passed
@tobi tobi changed the title Compile try to a native binary with Spinel try, compiled: Ruby CLI to a 705KB native binary Aug 13, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant