Flyweight TypeName / Namespace cached in a shared trie#2957
Merged
soutaro merged 1 commit intoMay 18, 2026
Conversation
Contributor
Author
|
This also improves the performance of steep (with kaigionrails/conference-app): |
049a778 to
543012c
Compare
Cache canonical TypeName / Namespace instances behind two
positional factory methods, and route every allocation site —
Ruby helpers, the C parser, the resolver, and the environment
— through them. Repeated construction of structurally equal
values returns the same object instead of allocating fresh.
Namespace[path, absolute]
TypeName[namespace, name]
Namespace interns into a per-`absolute` trie of nested Hashes
keyed on path Symbols. TypeName interns into a two-level Hash
keyed by canonical Namespace identity and name Symbol. Both
fast paths are lock-free; cache misses take a mutex.
The C parser calls `Namespace[]` / `TypeName[]` via
`rb_funcallv`; an in-C trie walk that read intern internals
through `rb_hash_lookup` was identical in wall time on Ruby 4.0+
and was dropped to avoid coupling the parser to the cache layout.
Measured against the kaigionrails/conference-app project's RBS
collection (3,903 type names), 1 warmup + 5 runs, median:
Ruby 3.4.9 master 1.247s wall / 0.63s user
branch 1.158s wall / 0.57s user (-7% wall, -10% user)
Ruby 4.0.4 master 1.175s wall / 0.57s user
branch 1.146s wall / 0.55s user (-2.5% wall, -4% user)
Live RBS::Namespace 51,436 → 6,904; RBS::TypeName 78,493 → 22,819
after `rbs list` on Ruby 4.0.
Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
543012c to
a35ba9f
Compare
soutaro
approved these changes
May 18, 2026
Member
soutaro
left a comment
There was a problem hiding this comment.
Hmm. This requires a difficult architectural decision. What if we want to multi-ractor processing in RBS?
Let's merge this for now, but we may revisit this when we want to make things in RBS parallel for more performance later.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Cache canonical TypeName / Namespace instances behind two positional factory methods, and route every allocation site — Ruby helpers, the C parser, the resolver, and the environment — through them. Repeated construction of structurally equal values returns the same object instead of allocating fresh.
Namespace[path, absolute]
TypeName[namespace, name]
Namespace interns into a per-
absolutetrie of nested Hashes keyed on path Symbols. TypeName interns into a two-level Hash keyed by canonical Namespace identity and name Symbol. Both fast paths are lock-free; cache misses take a mutex.The C parser calls
Namespace[]/TypeName[]viarb_funcallv; an in-C trie walk that read intern internals throughrb_hash_lookupwas identical in wall time on Ruby 4.0+ and was dropped to avoid coupling the parser to the cache layout.Measured against the kaigionrails/conference-app project's RBS collection (3,903 type names), 1 warmup + 5 runs, median:
Ruby 3.4.9 master 1.247s wall / 0.63s user
branch 1.158s wall / 0.57s user (-7% wall, -10% user)
Ruby 4.0.4 master 1.175s wall / 0.57s user
branch 1.146s wall / 0.55s user (-2.5% wall, -4% user)
Live RBS::Namespace 51,436 → 6,904; RBS::TypeName 78,493 → 22,819 after
rbs liston Ruby 4.0.