Skip to content

[Performance] Make in-guest Homebrew queries practical - #1222

Open
brandonpayton wants to merge 3 commits into
mainfrom
emdash/homebrew-perf-final-we7wo
Open

[Performance] Make in-guest Homebrew queries practical#1222
brandonpayton wants to merge 3 commits into
mainfrom
emdash/homebrew-perf-final-we7wo

Conversation

@brandonpayton

Copy link
Copy Markdown
Member

Why

Read-only Homebrew metadata queries in Kandelo took 20 to 32 seconds.
Warm queries fetched no bytes, yet repeated almost the entire startup,
load-path discovery, process, and syscall cost. The deployed rev22 shell
also lost the Chrome 149 renderer before one complete browser benchmark
round could finish.

This made ordinary commands such as brew info impractical and exposed
general Ruby, virtual filesystem (VFS), process-worker, and WebAssembly
compilation costs. The fix needs to preserve POSIX behavior,
authoritative VFS bytes, lazy integrity, and Node/browser parity.

What changed

  • Package Homebrew with a relocatable portable Ruby layout and its real
    upstream Bootsnap and msgpack gems.
  • Add general Wasm side-module support so native Ruby extensions use the
    ordinary dlopen() and dlsym() path.
  • Embed exact authenticated bottle receipt and formula metadata while
    retaining whole-bottle inventory and digest validation for lazy
    payloads.
  • Avoid redundant HostFS traversal only when the path is known to
    contain no symlink; retain full POSIX resolution as the fallback.
  • Keep four pristine process Workers ahead of demand. Every leased
    Worker remains one-shot and is terminated after its process retires.
  • Reuse only exact-content compiled WebAssembly.Module objects. Cache
    hits compare current VFS bytes and the cache is bounded to 16 entries,
    256 aliases, and 64 MiB of comparison bytes.
  • Add a deterministic in-guest Homebrew benchmark for Node and Chromium.
    It measures cold, booted-first, warm, and eager-image cases against
    closed local assets and audits guest network syscalls.

No Realm, memory, shared buffer, channel, or process state is reused.
The central kernel stays in its dedicated Worker. This change does not
alter the ABI.

Upstream patch accounting

There is no brew info shortcut and no patch to Homebrew discovery,
Ruby require, Bundler paths, or Bootsnap's cache algorithm.

The only new upstream-source patch adds wasm to three Bootsnap 1.24.5
supported-platform regular expressions. Bootsnap and msgpack otherwise
run their genuine upstream implementations. Kandelo's existing
Homebrew bottle tag/relocation and CRuby process-startup patches predate
this work.

Results

Measurements used an Apple M5 Max, Node 24.15.0, Chrome
149.0.7827.55, the published compatibility prefix, current tap inputs,
and three rounds. The committed measurement record contains every
artifact and tap digest.

  • Node warm brew info: 25.56 s on rev22 to 4.62 s, 5.54x faster.
  • Node cold boot plus first query: 27.05 s to 10.46 s, 2.59x faster.
  • Chromium candidate: 5.01 s warm and 10.23 s including cold boot.
  • Rev22 produced no valid complete Chromium result because its renderer
    crashed. No browser speedup ratio is claimed.
  • Every audited Brew command made zero guest network syscalls.
  • A cold query fetched seven closed assets totaling 28.04 MB. Warm
    queries fetched zero bytes.

The exact protected-main/candidate repository benchmark also showed:

  • Node clone, fork, and spawn improved 91%, 86%, and 89%.
  • Chromium clone, fork, and spawn improved 64%, 57%, and 39%.
  • WordPress remained within 1% except browser first response, which
    improved.
  • Node syscall latency increased 7.8% and file writes fell 9.4%.
  • Browser syscall latency increased from 14 to 18 us and file writes
    fell 11.1%.
  • Full-suite Node MariaDB results varied by 5% to 16%. An immediate
    isolated exact-artifact control ranged from 1.6% faster to 3.0%
    slower. This is reported as run-state sensitivity, not as a
    regression-free claim.

Distribution and runtime costs are:

  • Portable Ruby: 11.62 MiB compressed and 39.42 MiB expanded.
  • Eager bottle metadata: 27,920 compressed bytes.
  • Module comparison bytes: 44.85 MB for this workload, capped at 64
    MiB, plus V8-owned compiled code whose size is not exposed.
  • One live Ruby process still reaches about 289 MiB because CRuby
    reserves a 256 MiB object-shape cache contiguously under Wasm.

Validation

  • Full build.sh in scripts/dev-shell.sh.
  • ABI snapshot, header, and generated TypeScript consistency.
  • Focused Homebrew benchmark, three rounds on Node and Chromium.
  • Full repository benchmark, three rounds on Node and Chromium,
    followed by benchmarks/compare.ts.
  • Focused host suites: 231 tests.
  • Portable Ruby loaded real msgpack and Bootsnap native extensions.
  • SDK and package tests: 45 tests.
  • Wasm local-root-spill tests: 15 tests.
  • Program package index context check.
  • Browser deferred-tree parity: 6 cases across Chromium, Firefox, and
    WebKit.
  • Process memory and wasm64 browser coverage: 13 cases.

Remaining work

A warm text query still launches 159 processes, performs 55 execs, and
makes roughly 29,000 syscalls. About 2,500 failed opens are normal
Ruby/Bundler candidate probing whose ENOENT results must stay
truthful. General syscall batching, process reduction, Chromium
brew config, CRuby's object-shape reservation, and V8 compiled-code
accounting remain the largest measured follow-ups.

Run Homebrew through a relocatable portable Ruby layout with its real
Bootsnap and msgpack extensions, while preserving exact bottle receipts
and lazy payload integrity.

Prestart pristine one-shot process workers and cache only exact-content
compiled Wasm modules across them. Never reuse Realms, memories, shared
buffers, channels, or process state.

Add deterministic Node and Chromium query benchmarks, prefix-neutral
packaging, and VFS, loader, and SDK support for ordinary dynamic Ruby
extensions.
Launch a fresh browser for every focused Chromium sample.

Prevent prior renderer and Realm state from leaking across rounds.

Keep Worker, memory, and shared-buffer state isolated as well.

Reject masked Brew failures that resemble fast zero exits.
Record final three-round Node and Chromium query measurements.

Document the rev22 boundary and repository-wide comparison.

Capture root cause, artifact cost, and upstream patch accounting.

Rank remaining work by measured impact.
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