Skip to content

Add an incremental resolution benchmark - #932

Open
soutaro wants to merge 4 commits into
mainfrom
codex/incremental-resolution-monolith-bench
Open

Add an incremental resolution benchmark#932
soutaro wants to merge 4 commits into
mainfrom
codex/incremental-resolution-monolith-bench

Conversation

@soutaro

@soutaro soutaro commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

Summary

This adds a benchmark for incremental resolution. It covers both the lightest case—a no-op resolve—and one of the heaviest incremental operations—an ancestor change.

Ancestor changes

The benchmark defines two empty modules:

module BenchmarkMixinA
end

module BenchmarkMixinB
end

It then repeatedly replaces a virtual source file between the following two versions:

class ExampleService
  include ::BenchmarkMixinA
end
class ExampleService
  include ::BenchmarkMixinB
end

Replacing the included module invalidates the target class's ancestor chain and triggers ancestor recalculation for its transitive descendants.

Target classes can be selected by descendant count, allowing light and heavy ancestor changes to be compared.

Results

On a monolith containing approximately 1.6 million declarations:

Scenario Merge median Resolution median
No-op 933 ms
1 descendant 0.02–0.03 ms 600–800 ms
Approximately 10,500 descendants 63–75 ms 720–790 ms

Merge time increases substantially with the number of descendants, although it remains small in absolute terms. Resolution time stays within approximately the same range regardless of descendant count.

This does not necessarily mean that ancestor recalculation takes constant time. After resolution, the graph still contained 37,040 pending work items:

  • 34,092 constant references
  • 2,935 ancestor calculations
  • 13 definitions

Unresolved work is retained so that it can be retried after future edits. Consequently, even a no-op resolve revisits a large amount of pending work. This fixed cost dominates the additional ancestor recalculation and makes differences between targets difficult to observe in the overall resolution time.

How to run

First, find suitable target classes:

utils/bench-incremental-resolution --candidates /path/to/workspace

Run the benchmark for one or more classes reported by that command:

utils/bench-incremental-resolution \
  --target LightTarget \
  --target HeavyTarget \
  /path/to/workspace

Inspect the unresolved and pending graph state:

utils/bench-incremental-resolution \
  --resolution-stats \
  /path/to/workspace

The workspace can also be configured once through DEFAULT_BENCH_WORKSPACE:

export DEFAULT_BENCH_WORKSPACE=/path/to/workspace

utils/bench-incremental-resolution --candidates
utils/bench-incremental-resolution --target HeavyTarget
utils/bench-incremental-resolution --resolution-stats

@soutaro
soutaro marked this pull request as ready for review July 15, 2026 04:22
@soutaro
soutaro requested a review from a team as a code owner July 15, 2026 04:22
Comment thread rust/rubydex/benches/incremental_resolution.rs Outdated
Comment thread rust/rubydex/benches/incremental_resolution.rs Outdated
Comment thread rust/rubydex/benches/incremental_resolution.rs Outdated
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.

2 participants