Skip to content

Pool temporary buffers in the resolution linearization path - #998

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

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
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.

1 participant