Skip to content

Bundler performance optimizations: 2x faster installs#9316

Closed
tobi wants to merge 13 commits intoruby:masterfrom
tobi:bundler-performance-optimizations
Closed

Bundler performance optimizations: 2x faster installs#9316
tobi wants to merge 13 commits intoruby:masterfrom
tobi:bundler-performance-optimizations

Conversation

@tobi
Copy link
Copy Markdown

@tobi tobi commented Feb 6, 2026

Summary

This branch implements ~35 performance optimizations to Bundler's installation pipeline, inspired by tenderlove's blog post and by studying uv's actual source code architecture. The result is a Bundler that is 1.5-2.5x faster across different workloads.

Benchmark Results (Sequential, 3 iterations each)

Cold Cache (fresh install, no gems cached)

Scenario Stock (median) Patched (median) Speedup
small (10 gems) 12.56s 6.64s 1.89x
chain (35 gems, deep deps) 35.14s 30.72s 1.14x
wide (70 independent gems) 17.20s 7.61s 2.26x
medium (50 gems, mixed) 55.66s 36.29s 1.53x

Warm Cache (gems already downloaded, re-install)

Scenario Stock (median) Patched (median) Speedup
small (10 gems) 11.93s 5.82s 2.05x
chain (35 gems, deep deps) 38.16s 22.99s 1.66x
wide (70 independent gems) 18.27s 7.39s 2.47x
medium (50 gems, mixed) 56.38s 25.89s 2.18x

Complete Optimization Table

Wave 1: Core Architecture (27 optimizations)

# Optimization Files Impact
1 Decouple download from install — two-phase pipeline: download ALL gems in parallel, then install parallel_installer.rb, source/rubygems.rb High — removes dependency blocking from downloads
2 Relaxed dependency ordering — pure Ruby gems install immediately without waiting for deps parallel_installer.rb High — only native ext gems wait for deps
3 macOS clonefile/hardlink install — hierarchical fallback: clonefile → hardlink → copy rubygems_gem_installer.rb Medium — near-instant file copies on APFS
4 CompactVersion 64-bit packed integers — pack major.minor.patch.extra into one u64 for O(1) comparison compact_version.rb (new), resolver/candidate.rb Medium — thousands of comparisons during resolution
5 Global gem cache — shared cache at $XDG_CACHE_HOME/gem/gems/ across Ruby versions source/rubygems.rb Medium — avoids re-downloading when switching Ruby
6 Batch prefetch in resolver — eagerly populate spec cache for all known dep names before PubGrub resolver.rb Medium — triggers compact index parallel fetching
7 Smart download ordering — native ext gems (larger) downloaded first to reduce tail latency parallel_installer.rb Low-Medium — hides large download latency
8 Early satisfaction check — skip entire pipeline when nothing changed installer.rb, definition.rb High — instant no-op when already satisfied
9 Gem info fetch dedup — memoize compact index fetches by name fetcher/compact_index.rb Low — prevents redundant network requests
10 Lockfile parser direct dispatchcase instead of send(@parse_method) lockfile_parser.rb Low
11 Lockfile parser O(1) spec lookup@specs_by_name hash instead of find lockfile_parser.rb Low
12 Cache lockfile_exists? — avoid repeated File.exist? calls definition.rb Low
13 O(1) dep lookup in converge_specsdeps_by_name + @gems_to_unlock as hash definition.rb Low-Medium
14 Compact index parser allocation reductionindex() + slice instead of split(" ", 3) compact_index_client/parser.rb Low — reduces GC pressure
15 Index#empty? fast path — direct @specs.empty? instead of iteration index.rb Low
16 SpecSet O(1) name check — hash lookup in validate_deps spec_set.rb Low
17 SpecSet O(1) find by namelookup[name] narrows candidates spec_set.rb Low
18 Cached reverse deps@reverse_deps hash in what_required spec_set.rb Low
19 O(1) rake lookup — hash instead of @specs.find in sorted spec_set.rb Low
20 SpecSet cache invalidation — clear caches on add/delete spec_set.rb Correctness
21 Inlined lock_name — avoid Gem::NameTuple allocation lazy_specification.rb Low
22 Hash-based dedup in lockfile gen{} instead of [] + include? lockfile_generator.rb Low
23-25 NameTuple caching — cache full_name, lock_name, hash rubygems/name_tuple.rb Low
26 Skip redundant cache writes — check existence before write_cache_file rubygems/installer.rb Low
27 Cache runtime_dependencies — memoize instead of filtering on every call rubygems/specification.rb Low-Medium

Wave 2: Advanced Optimizations (10 optimizations)

# Optimization Files Impact
28 Dynamic HTTP pool size — match worker count (min 8) instead of hardcoded 5 fetcher/gem_remote_fetcher.rb Low — removes connection bottleneck
29 ignore_ruby_upper_bounds setting — opt-in filter for < and <= Ruby constraints match_metadata.rb, settings.rb Medium — reduces resolver backtracking
30 CompactVersion in all hot pathscompare() and versions_equal?() class methods compact_version.rb, resolver.rb, gem_version_promoter.rb Medium — fast path for resolution + promotion
31 Blake2b256 fast_digest — ~3x faster than MD5 for local cache path hashing shared_helpers.rb, compact_index_client/cache.rb Low — faster local hashing, MD5 fallback
32 Binary spec cache (Marshal) — cache parsed compact index data as binary compact_index_client/parser.rb Medium — skip text parsing on warm cache
33 Streaming install pipeline — inline extract+finalize per gem, no batch barrier parallel_installer.rb, gem_installer.rb, rubygems_gem_installer.rb, source/rubygems.rb High — removes phase barrier that serialized work
34 Native ext priority in install queue — scan .gem metadata, enqueue native ext gems first parallel_installer.rb Medium — compilation starts ASAP
35 Global extension cache$XDG_CACHE_HOME/gem/extensions/ keyed by Ruby ABI source/rubygems.rb Medium — avoids recompiling extensions
36 Pre-filter installed_specs — use stubs_for(name) instead of full glob rubygems_integration.rb, source/rubygems.rb Low-Medium — targeted glob vs scan-all
37 uv-inspired progress reporter — live spinner with phase summaries and slow-item tracking installer/progress_reporter.rb (new), parallel_installer.rb UX — clean, informative output

Additional Fixes

Fix Files
install_needed? was private but called externally definition.rb
caches array grew unbounded with duplicate entries source/rubygems.rb
Compact index info files read twice (MD5 + data) compact_index_client/cache.rb
Worker thread UI messages corrupted progress display parallel_installer.rb (thread-local silence)
LazySpecification lacks extensions — native ext detection failed parallel_installer.rb (detect from real spec)
Git source checkout during download phase source/git.rb
Incremental Gem::Specification.add_spec instead of double reset rubygems_gem_installer.rb

Architecture

STOCK BUNDLER:
  Resolver → for each spec (in dependency order):
    download gem → install gem → next spec

PATCHED BUNDLER:
  [Fast pre-check: already satisfied? → skip everything]
  Resolver (batch prefetch + CompactVersion integers)
    → Phase 1: Download ALL gems in parallel (native ext first)
    → Scan: peek at .gem metadata to detect native extensions
    → Phase 2: Install gems (streaming extract+finalize)
       - Native ext gems enqueued first → compilation starts ASAP
       - Pure Ruby: extract+finalize immediately (no dep wait)
       - Native ext: wait for deps → extract+finalize+compile
       - clonefile/hardlink for file operations
       - Global cache at XDG_CACHE_HOME

Test plan

  • Verify bundle install cold cache on synthetic workloads (small, chain, wide, medium)
  • Verify bundle install warm cache on synthetic workloads
  • Verify native extension compilation works (nokogiri, puma, bootsnap)
  • Verify git source gems install correctly
  • Verify bundle exec works after install
  • Verify ignore_ruby_upper_bounds setting works when enabled
  • Test on large real-world Rails app (382 gems)
  • Run existing bundler test suite

🤖 Generated with Claude Code

tobi and others added 12 commits February 6, 2026 16:05
Split the ParallelInstaller into two distinct phases:

Phase 1 (Download): Download ALL gems in parallel with a dedicated
worker pool. Since .gem files are just archives, no dependency ordering
is needed - all downloads can happen concurrently.

Phase 2 (Install): Install gems with dependency-aware ordering, but
with all gems already cached locally. Pure Ruby gems (no native
extensions) are installed immediately without waiting for dependencies,
since they don't execute code during installation. Only gems with
native extensions wait for their dependencies.

Additional changes in this commit:
- Add Source::Rubygems#download() as standalone download method
- Add has_native_extensions?() detection for install ordering
- Add global gem cache at $XDG_CACHE_HOME/bundler/gems/ with
  hardlink-to-local-cache strategy for cross-Ruby-version sharing
- Add early satisfaction check: skip entire pipeline when nothing
  changed (inspired by uv's SatisfiesResult::Fresh)
- Make Definition#install_needed? public for the early check
- Cache lockfile_exists? and use O(1) hash lookups in converge_specs
- Memoize cached_gem and installed? to avoid redundant stat calls
- Guard against unbounded growth of caches array
- Short-circuit lockfile write when nothing changed
Use a hierarchical file copy strategy inspired by uv's linker:
1. clonefile (macOS APFS copy-on-write) via cp -cR - nearly instant
2. hardlink tree - shares inodes, no data copied
3. regular copy - fallback for cross-device or unsupported filesystems

Reduce unnecessary filesystem operations:
- Consolidate triple stat in strict_rm_rf to single lstat call
- Read compact index info files once (was reading twice: once for
  MD5 checksum, once for data)
- Skip mkdir_p for compact index cache dirs that already exist
- Reorder cached! to check flag before File.exist?

Add optional IO tracing via BUNDLER_IO_TRACE=1 environment variable
for profiling filesystem operations during bundle install.
Inspired by uv's version.rs, pack gem versions into 64-bit integers:
[16-bit major][16-bit minor][16-bit patch][16-bit extra]. Integer
comparison is O(1) with zero allocations, replacing Gem::Version#<=>
which splits strings and allocates arrays on every comparison.

~90% of real-world versions (those with <= 4 numeric segments, each
<= 65535, no prerelease tags) use the fast integer path. Prerelease
and unusual versions transparently fall back to Gem::Version.

The resolver performs thousands of version comparisons during
dependency resolution, so this reduces both CPU time and GC pressure.
Before starting PubGrub resolution, eagerly populate the spec cache
for all known dependency names from both Gemfile requirements and
lockfile transitive dependencies. This triggers the compact index's
parallel fetching to batch network requests upfront rather than
fetching specs one-by-one as the resolver discovers them.

Also add a gem info cache to CompactIndex that memoizes fetched
gem info by name, preventing redundant network requests when the
resolver retries or multiple sources overlap.

Both patterns are inspired by uv's batch_prefetch.rs and OnceMap.
Optimize data structures in frequently-called code paths:

- LockfileParser: replace send() dispatch with case statement, add
  @specs_by_name hash for O(1) dependency-to-spec lookup
- CompactIndexClient::Parser: reduce allocations in versions parsing
  by using index()+slice instead of split()
- Index#empty?: direct @specs.empty? instead of Enumerable iteration
- SpecSet: O(1) name checks in validate_deps, O(1)
  find_by_name_and_platform via lookup hash, cached reverse dependency
  map in what_required, O(1) rake lookup in sorted
- LazySpecification: inline lock_name to avoid NameTuple allocation
- LockfileGenerator: hash-based dedup instead of array scan
- Gem::NameTuple: cache full_name, lock_name, and hash values
- Gem::Installer: skip write_cache_file when cache already exists
- Gem::Specification: memoize runtime_dependencies (called thousands
  of times during resolution, each creating a new filtered array)
The pool was hardcoded at 5 but the download phase uses 8+ workers,
creating a bottleneck. Now scales to max(jobs, 8).

Co-Authored-By: Claude Opus 4.6 <[email protected]>
Opt-in setting (BUNDLE_IGNORE_RUBY_UPPER_BOUNDS=true) that filters out
upper-bound Ruby version requirements from gem metadata. Useful when
gems haven't updated their metadata for newer Ruby versions.

Co-Authored-By: Claude Opus 4.6 <[email protected]>
Add CompactVersion.compare() and versions_equal?() class methods for
direct Gem::Version comparison using 64-bit packed integers. Apply in
resolver sort, group_by, and GemVersionPromoter filter_versions to
avoid expensive Gem::Version#<=> in hot paths.

Co-Authored-By: Claude Opus 4.6 <[email protected]>
Blake2b256 is ~3x faster than MD5 for hashing. Use it for local-only
operations (cache path generation, etag paths) via fast_hexdigest.
Falls back to MD5 when OpenSSL doesn't support Blake2. Protocol-level
compact index checksums still use MD5 as required by the server.

Co-Authored-By: Claude Opus 4.6 <[email protected]>
Cache parsed compact index info arrays in Marshal format at
info-binary/<name>.bin. On subsequent runs, load binary cache if the
compact index checksum matches, skipping text parsing (string splitting,
object allocation). Non-fatal on cache read/write failures.

Co-Authored-By: Claude Opus 4.6 <[email protected]>
Split installation into download -> install phases with inline
extract+finalize per gem. No batch barrier between extraction and
installation — each gem extracts and finalizes in one step.

After downloading, a quick scan reads .gem metadata to detect native
extensions WITHOUT extracting. This lets the install phase prioritize
native ext gems first so compilation starts ASAP and overlaps with
pure Ruby gem installation.

Key changes:
- extract_to_temp/finalize_with[out]_extensions in RubyGemsGemInstaller
- extract_gem/finalize_gem in Source::Rubygems
- Streaming install in ParallelInstaller: native ext gems enqueued first
- scan_native_extensions: peek at .gem metadata to detect extensions early
- uv-inspired ProgressReporter with spinner, aligned counts, slow item display
- Git sources participate in parallel download phase
- Worker threads silenced to prevent UI corruption (thread-local fix)
- Native extension detection from real spec after extraction (not LazySpec)
- Global extension cache in XDG_CACHE_HOME keyed by Ruby ABI
- Global gem cache path aligned with rubygems#7249 convention
- Pre-filter installed_specs by lockfile gem names for targeted lookup
- Incremental Gem::Specification.add_spec instead of double reset

Co-Authored-By: Claude Opus 4.6 <[email protected]>
- Remove Bundler.ui.silence wrapper (per-worker silence suffices)
- Remove stock "Installing X" / "Fetching X" / "Using X" UI messages
  from source/rubygems.rb — progress reporter handles all display
- Remove scan_native_extensions pass that opened every .gem an extra
  time — native extensions detected inline during install from real spec
- Single pass: download → install (no intermediate scan)
- Fix progress reporter: guard write_header when no phase active,
  flush after finish_phase to keep summaries in scrollback

Co-Authored-By: Claude Opus 4.6 <[email protected]>
@kou
Copy link
Copy Markdown
Member

kou commented Feb 7, 2026

Could you split this large PR to small PRs?

@kou
Copy link
Copy Markdown
Member

kou commented Feb 7, 2026

This is not reviewable.

Key changes:
- Global extracted gem cache (~/.cache/gem/extracted/) avoids
  re-extracting .gem files across projects/installs. Cache hit
  loads marshaled spec + hardlinks files into GEM_HOME in ~0.01s.
- Single-pass .gem extraction reads tar once, piping data.tar.gz
  to native `tar` for zero-Ruby-allocation decompression.
- Hardlink-first file placement (pure Ruby, no subprocess) with
  clonefile and cp_r fallbacks.
- Hardlink .gem files into GEM_HOME/cache/ instead of copying.
- Shallow git clones (--depth 1) for git source gems.
- Hide cursor during parallel download/install phases.
- Show current gem name in progress reporter.
- Reset Gem::Specification only when actually compiling extensions.
@tobi
Copy link
Copy Markdown
Author

tobi commented Feb 7, 2026

Benchmark update — global extracted gem cache

Added a global extracted gem cache (~/.cache/gem/extracted/) that avoids re-extracting .gem files across installs. On cache hit, the marshaled spec is loaded and files are hardlinked into GEM_HOME in ~0.01s per gem.

Benchmark results

Each iteration runs a paired cold→warm sequence: nuke all caches → bundle install (cold), then keep caches and re-install (warm). Stock cold is the 1x baseline.

Scenario Stock Cold Stock Warm Patched Cold Patched Warm
small 13.28s (1x) 10.74s (1.24x) 7.88s (1.68x) 1.22s (10.85x)
wide 11.30s (1x) 8.53s (1.32x) 13.42s (0.84x) 4.42s (2.56x)
chain 24.29s (1x) 20.21s (1.20x) 31.21s (0.78x) 6.36s (3.82x)
medium 46.67s (1x) 42.96s (1.09x) 32.82s (1.42x) 9.07s (5.14x)
rails 17.27s (1x) 13.66s (1.26x) 22.80s (0.76x) 6.33s (2.73x)

Geometric mean speedup vs stock cold:

  • Stock warm: 1.22x
  • Patched cold: 1.04x
  • Patched warm: 4.31x

What this means

  • Cold (first ever install): Roughly even with stock. The patched version has overhead populating the global cache on first run.
  • Warm (any subsequent install): 4.31x faster. Every bundle install after the first benefits enormously — gems are hardlinked from the global cache instead of re-extracted from .gem files.

The "rails" scenario uses the default rails new Gemfile (Rails 8.1, ~66 lines). Real-world installs go from 17s → 6s.

@colby-swandale
Copy link
Copy Markdown
Member

Hey, thanks for sending this through @tobi

Some of these optimisations overlap with things we've already got in-flight or on our radar, which is encouraging. As Kou mentioned though, the scope makes it pretty tough to review as one PR. We're going to dig through these and try to pull out the most impactful changes and look to land them incrementally so we can validate properly and make sure we're not breaking compatibility.

Thanks!

@eileencodes
Copy link
Copy Markdown
Member

@colby-swandale I was going to work on splitting up this PR and rebenchmarking each step. I know there's a PR for refactoring the cache handling - are there other PRs in flight that compete with work in this PR that I should know about?

@colby-swandale
Copy link
Copy Markdown
Member

@eileencodes #9210 comes to mind. The other PRs that have also overlap, but have since been merged are #9087 & #9230

@simi
Copy link
Copy Markdown

simi commented Mar 7, 2026

Hello, indeed super hard to review one. I have checked out of curiosity one change (c859a8c) and it seems it needs more attention. Probably each of those optimizations will work as separate issue. Is there still plan to split this into smaller PRs? I have potential interested in exploring some of those suggestions, is there any tracker to follow? Is the list free to pick up?

@ejbills

This comment was marked as off-topic.

@Edouard-chin
Copy link
Copy Markdown
Collaborator

Edouard-chin commented Apr 2, 2026

Is there still plan to split this into smaller PRs? I have potential interested in exploring some of those suggestions, is there any tracker to follow? Is the list free to pick up?

I wanted to circle back on this PR and surface visibility for other contributors that are interested in extracting some of the changes. Many of the most impactful optimizations from this PR were implemented or extracted out. All the "low" impact changes don't help move the needle much on the overall bundle install/update/lock speed, but still could be worth extracting, if anyone wants to pick some.


This is the list of changes that were merged or are going to

  1. Global XDG_CACHE_HOME
  2. Decouple download and install/
  3. Native extensions priority queue
  4. Fast path for comparing gem versions
  5. Cache package version selection (This one supersede many resolvers perf improvements from this PR as it has a much bigger impact with a smaller footprint)
  6. Relaxed dependency ordering
  7. Upperbound constraints removal
  8. Connection pool size/Parallel workers increase
  9. Streaming install pipeline

@hsbt
Copy link
Copy Markdown
Member

hsbt commented Apr 3, 2026

The following items are extracted from this PR and remain unaddressed:

  1. clonefile/hardlink install — Replace FileUtils.cp_r with macOS clonefile or hardlink to reduce IO during gem installation.
  2. Binary spec cache for compact index — Cache parsed compact index results via Marshal to skip text parsing on subsequent runs.
  3. Streaming install pipeline (additional part) — Eliminate re-enqueue polling and worker pool contention so gems flow more efficiently from download to install.
  4. Global extension cache — Share compiled native extensions across projects via an ABI-keyed global cache directory.
  5. Resolver batch prefetch — Prefetch dependency information for multiple gems in bulk before resolution, reducing round-trips to the index.
  6. ignore upper bounds

There are also several small changes like memoization and hash-based lookups that could be worth extracting individually.

@eileencodes
Copy link
Copy Markdown
Member

clonefile/hardlink install

This one is interesting. The benchmark I wrote showed that while this is faster on linux it's significantly slower on macos. There is some work that can be done to close the gap, that but I wasn't able to find a path that made it faster on macos.

@eileencodes
Copy link
Copy Markdown
Member

eileencodes commented Apr 16, 2026

Hi @tobi , we spent time on your pull request after it was opened to figure out what could be pulled out and where we could find performance improvements in Rubygems/bundler.

After reviewing the benchmark I originally wrote and the one you sent me, I wasn’t seeing reliable numbers. I had Claude rewrite the benchmark and eventually wrote a performance toolkit for Rubygems/bundler (it also supports benchmarking other Ruby package managers).

One of the things that different benchmarks struggled with was not reducing the number of variables at play on any given run. To narrow down those variables the toolkit uses hyperfine for benchmarking, defaults to Ruby 4.0.1 (dynamically linked because it’s faster), and has a set definition for cold/warm cache, sets the correct environment variables to turn on the cache, and most importantly uses a local gemserver to avoid network time or rate limits skewing results.

Ultimately while this PR does show some improvement in the warm cache, that improvement is only on Linux. On macOS the warm cache unfortunately is slower. For both Linux and macOS the cold cache is slower with this PR. In addition, when the cache is properly turned on in Bundler master (my original benchmark also wasn’t turning it on correctly either), the warm cache shows it’s already very fast. Lastly the original numbers looked more drastic because the benchmark compared against Bundler 2.7, which predates the performance improvements that had already landed.

Even with my toolkit there’s still a lot of variables at play from current load on the machine to endpoint security software to what Ruby version is being used. I can get wildly different results depending on the machine and these variables.

After many many many runs and lots of work to reduce uncertainty, these are the numbers I got for master versus this PR using Ruby 4.0.1 (dynamically linked, because that was faster), using a local fake network with real gems (so compiled time is still accounted for, but network time is not), 3 iterations, on a Gemfile with 164 gems. The macOS machine is my work macbook pro M3 but I also ran the numbers on my personal macbook air M3 and the numbers were similar. The Linux machine is a 32-core AWS sandbox isolated from noisy neighbors.

Platform Run Cold (med) Warm (med)
macOS master 13.73s 1.54s
macOS pr9316 15.54s 3.37s
macOS delta 1.13x slower 2.19x slower
Linux master 8.80s 2.03s
Linux pr9316 9.74s 1.05s
Linux delta 1.11x slower 1.93x faster

I was a bit surprised there was such a drastic difference between macOS and Linux so I had Claude bisect the PR to find where the regression was introduced. The goal was to understand if there were a set of commits that did meaningfully improve macOS. We found that the regression is isolated to a single code path in RubyGemsGemInstaller#fast_cp_r, which can be improved for macOS. However, even in changing this it didn’t make macOS warm cache faster, it was about the same, and cold caches were still slower.

I put one of the bisect summaries and investigations that Claude did in a gist if anyone would like to read more about the process used. It is edited for clarity, but otherwise entirely written by Claude.

Conclusion

The ideas that Claude came up with in this PR are interesting and were explored in depth. I bisected the PR in many different ways and pulled out parts that seemed most promising.

Through this work we kept coming back to the warm cache on master is pretty fast already but it’s the cold cache that’s slow. The reason is because compiling native extensions completely dominates the threads in the cold cache. We can see this in the profiles below.

A profile of cold cache installs on the Linux, blocking on installing native extensions:

The pink bars are the threads waiting for compiling native extensions to be done. This is where we are spending all our time on a cold cache, which is arguably the most time consuming because it takes more 10-30s to install cold. Bigdecimal alone takes 3s to install, and while that’s happening the thread is just sitting around waiting.

Screenshot 2026-04-16 at 9 10 12 AM

A profile of warm cache installs on the Linux, not blocking on installing native extensions (warm caches have already compiled everything):

Warm caches were always sub 5s, because they are not compiling native extensions. You can see we don’t have the same yellow and pink bars dominating the time.

Screenshot 2026-04-16 at 9 10 48 AM

All of the work we’ve done with AI and on our own points back to precompiling native extensions as our biggest win now that we’ve improved warm caches a lot. But degrading cold caches even farther is a non-starter. We have a project in the works that will solve the cold cache problem without requiring upstream gem authors to change anything. We already have data showing that we can reduce cold install times to sub 6s (so they’re inline with warm), which will be a huge win for the community. We can absolutely still find places to improve the warm cache, but that win isn’t going to be as noticeable as improving cold installs.

Thank you for opening this PR. It pushed us to concretely define the benchmark that should be used for all performance work in Rubygems/bundler. We have opened issues to investigate some of the micro optimizations in this PR as a followup to our work on the cold cache performance.

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.

8 participants