Skip to content

Pool temporary buffers in the resolution linearization path - #998

Closed
dersam wants to merge 1 commit into
mainfrom
buffer-pooling-allocation-reuse
Closed

Pool temporary buffers in the resolution linearization path#998
dersam wants to merge 1 commit into
mainfrom
buffer-pooling-allocation-reuse

Conversation

@dersam

@dersam dersam commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

Summary

The Resolver now pools temporary buffers used during ancestor linearization, instead of allocating new ones for each linearization. Three internal functions were refactored from returning owned collections to writing into caller-provided buffers:

  • linearize_parent_ancestorslinearize_parent_ancestors_into
  • linearize_superclasslinearize_superclass_into
  • linearize_mixins (signature change: takes &[Mixin] and &mut VecDeque instead of owning and returning)

Four pools were added to Resolver:

  • chain_pool: Vec<Vec<Ancestor>> — parent ancestor chain buffers (one per recursion level)
  • mixin_pool: Vec<Vec<Mixin>> — collected mixin buffers
  • deque_pool: Vec<VecDeque<Ancestor>> — linearized prepend/include deques
  • spare_context: Option<LinearizationContext> — reused linearization context (hash sets retain capacity)

The final ancestors Vec is also pre-allocated with exact capacity (Vec::with_capacity(final_len)) to eliminate reallocations during assembly.

rubydex _998 _ Ancestor Linearization_ Buffer Pooling

Benchmark Results

Shopify core

Release mode, Apple M3 Pro. 111,463 Ruby files, ~1.14M declarations. Each binary ran 20 times sequentially with no concurrent heavy processes. Welch t-test for significance.

Metric Branch Mean Median SD Min Max 95% CI
Max RSS (MB) main (462de8e) 5367.69 5386.97 103.36 5121.93 5527.82 [5319.32, 5416.07]
Max RSS (MB) branch (c89869e) 5378.40 5384.99 74.75 5250.20 5518.68 [5343.42, 5413.38]
Resolution (s) main (462de8e) 13.86 13.85 0.57 13.02 15.13 [13.59, 14.13]
Resolution (s) branch (c89869e) 12.34 12.07 0.39 11.86 12.97 [12.15, 12.52]
Total (s) main (462de8e) 20.49 20.44 0.70 19.47 22.13 [20.16, 20.82]
Total (s) branch (c89869e) 18.61 18.19 0.65 17.90 19.82 [18.31, 18.92]
Metric Delta (mean) Delta (%) p-value Significant?
Max RSS +10.71 MB +0.2% 0.707 No
Resolution -1.52s -11.0% <0.001 Yes (p < 0.001)
Total -1.88s -9.2% <0.001 Yes (p < 0.001)
Raw per-run data (20 runs each)

Main (462de8e):

Run RSS (MB) Resolution (s) Total (s)
1 5423.89 13.849 20.89
2 5362.37 13.845 20.15
3 5322.48 13.794 20.73
4 5517.57 13.459 19.90
5 5273.64 13.626 20.72
6 5146.09 13.307 20.16
7 5121.93 13.018 19.52
8 5321.56 13.125 19.71
9 5375.46 13.211 19.47
10 5446.75 14.446 20.86
11 5391.81 14.317 20.88
12 5400.92 13.539 20.20
13 5452.59 13.923 20.28
14 5289.28 14.108 20.42
15 5405.70 14.032 20.46
16 5441.78 14.039 20.62
17 5358.28 14.899 21.95
18 5391.76 15.135 22.13
19 5382.18 13.291 19.82
20 5527.82 14.249 20.94

Branch (c89869e):

Run RSS (MB) Resolution (s) Total (s)
1 5335.60 12.568 19.13
2 5305.54 12.967 19.40
3 5281.59 12.555 18.98
4 5264.50 12.821 19.45
5 5385.34 12.813 19.37
6 5294.60 12.880 19.82
7 5347.43 12.781 19.06
8 5348.87 12.487 19.11
9 5250.20 12.806 19.26
10 5429.28 12.062 18.17
11 5384.64 12.081 18.20
12 5382.18 12.062 18.10
13 5518.68 11.956 18.05
14 5423.18 12.062 18.02
15 5426.68 12.035 18.07
16 5399.40 12.025 17.90
17 5427.07 12.024 18.12
18 5504.71 11.910 17.90
19 5448.45 11.864 18.07
20 5410.06 11.986 18.07

@dersam
dersam requested a review from a team as a code owner August 10, 2026 18:50
@dersam
dersam marked this pull request as draft August 10, 2026 19:26
@dersam
dersam force-pushed the buffer-pooling-allocation-reuse branch from 40106c9 to b6c349a Compare August 11, 2026 15:40
Extract the buffer pooling / allocation reuse optimization from PR #983
into a standalone branch. The linearization recurses in strict
last-in-first-out order, thus a plain list of spare buffers on the
Resolver is enough.

Changes (resolution.rs only):
- Add chain_pool, mixin_pool, deque_pool, and spare_context to Resolver
- Add take/return methods for each pool
- Add LinearizationContext::reset for context reuse
- ancestors_of reuses a spare LinearizationContext instead of allocating
- linearize_ancestors builds the parent chain, mixin list, and result
  chain in pooled buffers; changed chains go into exact-size Vecs so
  the graph never holds spare capacity
- linearize_parent_ancestors -> linearize_parent_ancestors_into writes
  into a caller-supplied buffer instead of returning an owned Vec
- linearize_superclass -> linearize_superclass_into does the same
- linearize_mixins takes &[Mixin] and Option<&[Ancestor]> and writes
  into caller-supplied VecDeques instead of allocating its own

No behavior change: the graph is identical. All 1149 Rust tests pass,
clippy is clean, and cargo fmt --check is clean.
@dersam
dersam force-pushed the buffer-pooling-allocation-reuse branch from b6c349a to c89869e Compare August 11, 2026 15:47
@dersam
dersam marked this pull request as ready for review August 11, 2026 17:12

@st0012 st0012 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While I understand what it optimizes for, I feel the gain doesn't justify the introduced complexity to the algorithm.
Also these additional pools will likely make it harder when we want to parallelize resolution?

@dersam

dersam commented Aug 17, 2026

Copy link
Copy Markdown
Contributor Author

While I understand what it optimizes for, I feel the gain doesn't justify the introduced complexity to the algorithm. Also these additional pools will likely make it harder when we want to parallelize resolution?

That's a good point, I wasn't aware parallel resolution was being explored.

The additional complexity would definitely make parallel resolution more complex, and any gains it introduces are outweighed by what parallelization would give us.

@dersam dersam closed this Aug 17, 2026
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