Skip to content
This repository was archived by the owner on Jun 8, 2026. It is now read-only.

Homebrew/brew-rs

Folders and files

NameName
Last commit message
Last commit date

Latest commit

ย 

History

296 Commits
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 

๐Ÿฆ€ brew-rs (Rust frontend experiment for Homebrew)

brew-rs was an experiment in replacing parts of Homebrew's Ruby frontend with Rust. It aimed to make common Homebrew commands significantly faster while preserving Ruby-compatible output and behaviour.

The experiment was abandoned. Local benchmarks showed that Rust was faster for narrow, API-backed bottle fetch batches, especially when archive data was already cached. That did not translate into a better Homebrew install path. When installs included the full Homebrew work of pouring bottles, linking files, writing metadata, running health checks and preserving existing semantics, Ruby was faster on representative fully native comparisons.

The Rust frontend was also only meaningful when it avoided delegation entirely. Delegating back to Ruby added handoff cost and did not answer whether a fully Rust implementation could outperform a fully Ruby implementation. Once delegated paths were excluded, the strongest Rust result was bottle fetching, not installing.

Current Status

This repository now serves as a record of the Rust frontend experiment and the benchmark work that ended it. The Rust code can still be useful for comparison, but it is no longer considered a shipping direction for Homebrew performance work.

The performance focus moved back to Ruby. The high-level goal became to improve brew install by reducing the time before useful network or disk I/O begins, using API-backed bottle metadata earlier and reducing overhead in simple bottle fetch paths without duplicating Homebrew's install semantics in another frontend.

Benchmark Summary

Representative zero-delegation benchmarks in a staged local Homebrew prefix showed this split:

Operation Ruby Rust Result
Fetch 100 bottles, archive cold 13.07s 11.42s Rust 1.14x faster
Fetch 100 bottles, archive warm 1.513s 0.379s Rust 3.99x faster
Fetch 6 bottles, archive cold 2.274s 1.471s Rust 1.55x faster
Fetch 6 bottles, archive warm 0.842s 0.313s Rust 2.69x faster
Install 50 simple bottles, archive warm 8.87s 93.54s Ruby 10.5x faster
Install 50 simple bottles, archive cold 75.71s 132.27s Ruby 1.75x faster
Install 6 bottles, archive cold 5.287s 7.632s Ruby 1.44x faster
Install 6 bottles, archive warm 1.924s 6.009s Ruby 3.12x faster

The useful conclusion was not that Rust could never beat Ruby at individual operations. It could. The conclusion was that the operations where Rust won were too narrow to justify a separate frontend, while the operations users most care about for brew install remained faster in Ruby.

Warm-cache reinstall-style benchmarks were treated as synthetic. They mostly measured repeating work after bottles and metadata were already local, rather than the more common path where Homebrew resolved metadata, downloaded missing bottles, poured kegs, linked files, wrote tabs and handled postinstall work.

Benchmarks based on a separate content-addressed store were also not treated as evidence that Homebrew could be made faster by swapping frontends. They measured a different system: one that could skip Homebrew's Cellar layout, tabs, links, caveats, postinstall semantics, cask behaviour, source fallback, tap logic and auditability. That can make a demo look quick, but it does not show a faster brew install unless it preserves the compatibility work users expect from Homebrew.

The comparison that mattered was fully Ruby versus fully Rust, zero delegation, with cold archive I/O included.

Potential Ruby Performance Improvements

The Rust work informed several ideas for improving the Ruby frontend directly:

  • Start useful I/O earlier in brew install.

    • Focus on the time between process start and the first bottle manifest or bottle archive transfer, not only total command time.
    • Use brew prof install FORMULA to separate startup, require, formula resolution and download enqueue costs.
    • Compare with hyperfine --warmup 0 in archive-cold and archive-warm modes, removing the target brew --cache --formula FORMULA paths before cold runs.
    • Treat Library/Homebrew/cmd/install.rb, formula_installer.rb, fetch.rb and download_queue.rb as the main flow to shorten before touching pour/link correctness.
  • Use API-backed bottle metadata before full Formula inflation where possible.

    • Investigate FormulaStruct use for simple homebrew/core bottles so Ruby can decide bottle URLs, tags and SHA256 values without building every full formula object up front.
    • Keep full Formula objects for dependency resolution, requirements, postinstall, caveats, tabs and non-trivial formula logic.
    • Compare brew fetch FORMULA... and the fetch phase of brew install FORMULA... for 1, 10, 50 and 100 formula batches.
    • Check whether Formulary.factory, args.named.to_formulae_and_casks and repeated bottle tag simulation dominate simple bottle fetches.
  • Make immutable bottle cache checks cheaper.

    • GHCR bottle blobs with API-provided SHA256 metadata should not need the same freshness path as arbitrary URLs, mirrors, casks, source tarballs and VCS strategies.
    • Profile CurlDownloadStrategy#resolve_url_basename_time_file_size, Utils::Curl.curl_headers, AbstractFileDownloadStrategy#cached_location and checksum verification.
    • Measure the cost of Pathname.glob cache discovery and curl header probes on warm-cache brew fetch runs.
    • Preserve current generic download_strategy behaviour for non-bottle URLs, source fallback, mirrors, content-disposition filenames, partial downloads and cask-specific handling.
  • Use brew update to improve Ruby startup and load performance.

    • Prewarm stable Bootsnap caches for the brew install and brew fetch load graph after update work that already changes Homebrew's Ruby files.
    • Avoid cache keys that churn on every checkout path, timestamp or vendor state change.
    • Compare the first command after brew update with and without warmed Bootsnap caches using hyperfine --warmup 0.
    • Use brew prof --stackprof install FORMULA to check whether require/load cost actually moved, rather than only hiding it behind a warm shell.
  • Add first-class phase timing around real install work.

    • Track planning, API metadata load, full formula inflation, dependency resolution, download enqueue, curl headers, curl body, checksum, symlink, extraction, pour, link, tab writing, cleanup and postinstall.
    • Keep timings opt-in and machine-readable enough to compare repeated runs.
    • Use the phase data to interpret brew prof output instead of relying on wall-time tables alone.
    • Make cold-cache tests explicit: metadata cold, archive cold, archive warm and fully warm are different workloads.

About

๐Ÿ’€ Experimental Rust frontend for Homebrew/brew (deprecated)

Resources

License

Code of conduct

Security policy

Stars

Watchers

Forks

Contributors