Lazy page index in RDoc::Store (42.7% perf improvement) - #1811
Conversation
Documentation previewCommit: |
There was a problem hiding this comment.
🟢 Approval recommended
The indexed lookup preserves existing semantics and is covered across mutation and loading paths.
Pull request overview
Adds a lazy page lookup index to improve repeated page resolution performance while preserving first-match behavior.
Changes:
- Indexes page and base names on first lookup.
- Invalidates the index when files change or load.
- Adds performance and invalidation tests.
File summaries
| File | Description |
|---|---|
lib/rdoc/store.rb |
Implements and invalidates the lazy page index. |
test/rdoc/rdoc_store_test.rb |
Tests scaling and cache invalidation. |
Review details
- Files reviewed: 2/2 changed files
- Comments generated: 0
- Review effort level: Balanced
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
tompng
left a comment
There was a problem hiding this comment.
Thanks, this is a solid win and the first-match test is much appreciated.
One thought, from context that isn't visible in the diff: Store#page is only
reached from CrossReference#resolve, i.e. during generation, which always runs
after Store#complete (rdoc, rdoc --server) or Store#load_all (ri servlet).
@unique_classes already relies on exactly this and is built in complete.
So the index could be built once in complete and at the end of load_all,
with no invalidation on add/remove at all. That also removes the three
@page_index = nil lines, which are easy to miss if another mutation path is
added later. Something like:
def complete(min_visibility)
...
@page_index = build_page_index
end
# (A comment that `complete` is needed to use this method)
def page(name)
# This will raise error if page_index isn't built yet.
# It's OK and it should, so that we can detect invalid usage of this method
@page_index[name]
end|
@tompng Interesting proposal, but I don't think this will work out.
RubyGemsHook does not work with any of these finalization approaches. It delegates parsing to This is a Codex explanation: Call FlowFor the test gem, # test/rdoc/rdoc_rubygems_hook_test.rb:15
s.extra_rdoc_files = %w[README]
# lib/rdoc/rubygems_hook.rb:168-170
args = @spec.rdoc_options
args.concat @spec.source_paths
args.concat @spec.extra_rdoc_filesAfter option parsing, it creates a store and parses every selected file: # lib/rdoc/rubygems_hook.rb:198-200
@rdoc.options = parse_options
@rdoc.store = Store.new(parse_options)
@rdoc.parse_files parse_options.files
# lib/rdoc/rdoc.rb:337-343
top_level = @store.add_file filename, relative_name: relative_path_for(filename)
parser = Parser.for top_level, content, @options, @stats
parser.scanFor a plain file such as # lib/rdoc/code_object/top_level.rb:68-71
def parser=(val)
@parser = val
@store&.cache_text_file(relative_name)
@parser
endFinalization DifferenceNormal # lib/rdoc/rdoc.rb:530-552
file_info = parse_files @options.files
# ...
@store.complete @options.visibility
# ...
generate
# lib/rdoc/rubygems_hook.rb:203-207
document 'ri', options, @ri_dir
document 'aliki', options, @rdoc_dirTherefore, its store contains pages and classes, but Why Existing Tests May Miss ItExact filenames such as # lib/rdoc/cross_reference.rb:204-205
ref = @store.page name if not ref and name =~ /^[\w.\/]+$/At that point, the new guard raises because neither |
|
You're right, and thanks for checking this properly. I missed the RubyGemsHook path. RubyGemsHook skipping |
RDoc::Store#pagepreviously scanned every known file for each page lookup, including repeated misses.This adds a lazily built hash index keyed by both
page_nameandbase_name, preserving existing first-match behavior when names collide.The index is invalidated whenever files are added, removed, or bulk-loaded. The implementation remains private to
RDoc::Storeand adds no public API or configuration.Benchmark
For testing I used latest rdoc(master) + rdoc-markdown gem. Generation was done on a rails codebase.
Total time to generate documentation decreased from 88.52s to 50.76s (42.7%).