initialize: improve slot allocation - #97
Closed
jll63 wants to merge 0 commit into
Closed
Conversation
|
An automated preview of the documentation is available at https://97.openmethod.prtest3.cppalliance.org/libs/openmethod/doc/html/index.html If more commits are pushed to the pull request, the docs will rebuild at the same URL. 2026-09-02 02:26:55 UTC |
jll63
force-pushed
the
fix/slot-allocation
branch
from
September 2, 2026 22:35
b8b8d48 to
767d6a5
Compare
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.
(Written by Claude Code, on behalf of @jll63.)
Closes #19.
Rebased on
developafter #94 landed: the order-agnostic tests below rely on the complete computation oftransitive_basesthat #94 brought. The branch is six commits: the rename of the test file, the allocator, its CI fixes (theregister_classesname clash under C++26 and/bigobjon MSVC), the write-up inSLOT_ALLOCATION.md, the dedicated tree pass, and a rework of the test file that cuts its compile time by more than half.The allocator
Every remark in #19 is taken.
Pull, do not push. A slot is off limits to a class if any class in its cone (itself and everything deriving from it) already holds the slot in its v-table. That is exactly the set of colliding slots: two parameters conflict when some class inherits both, and that class is in the cone of each. So
unavailable = ∪ used_slots over the cone, computed once per class, andreserved_slotsplus the three nested loops that pushed it to the transitive bases of the transitive derived classes are gone. After an assignment a single bit is propagated, to the cone. Correctness no longer depends on the order in which classes are visited; only compactness does.Widest cone first. Classes are allocated in decreasing size of their cone, while the v-tables their slot must fit into are still alike; a class whose slot must fit many v-tables is the expensive one to place late, once those v-tables have grown apart. A base's cone strictly contains its derived classes' cones, so bases first follows. Between unrelated classes with cones of the same size the deeper one goes first (it has an inherited range to extend and fewer options), and registration order decides only the ties left after that.
Cheapest growth. The slot chosen for a parameter is the free one that adds the fewest entries to the v-tables of the cone: a hole inside a v-table's range costs nothing, growth beyond either end costs one per slot, a class with no v-table yet costs one wherever the slot lands. Each v-table's cost is flat inside its range and grows by one per slot away from it, so the sum over the cone is computed for every candidate slot in one sweep, in O(cone + slots). This subsumes the three cases from the issue: filling holes, growing up or down (counting the derived classes' ranges, not just the class's own), and placing a root next to, or inside a hole of, its descendants' ranges.
Trees keep their own pass. A root whose cone holds no multiply-derived class heads a tree, disjoint from every other cone (a class in two cones would have two direct bases somewhere above it). The old depth-first walk allocates it in one pass, linear in its size, with every v-table dense from slot 0; the general algorithm would produce the same layout at a higher cost, so it skips those classes.
SLOT_ALLOCATION.mdin the repository gives the full account of both allocators, the measurements, and their complexity.Tests: no assumption on registration order
A program does not control the order in which classes register (static construction across translation units and modules), so the tests must not assume one.
test_compiler.cppis renamedtest_slot_allocator.cppand its slot tests are rewritten on a harness that registers each hierarchy once and, for every order under test, unlinks the hierarchy's class records from the registry's list and links them back in that order: every permutation for the small lattices, a fixed sample of orders (listed, reversed, seeded shuffles) for the larger ones. Hierarchies share a registry, one for the small lattices and one for the real ones; being separate components of the class graph, they do not affect each other's result. Every run checks that no two parameters share a slot in any v-table, that every v-table starts at its first slot and ends at its last, and the total v-table size. The new allocator makes that total the same in every order for every hierarchy tested, so the tests pin it exactly.Beyond the small lattices (tree, virtual diamond, hole avoided, grow down, cone-aware direction, several parameters per class, method-less classes, disconnected components, and a unit test of the chooser on plain bitsets), five real hierarchies are exercised with one unary method per class, with the source and license named in the test:
basic_iosvirtual;socketserver(PSF License 2.0), 18 classes, eight mix-in combinations, plus a binary method across its two trees;ContextMixinvirtual;conditionanderrorvirtual;collections.abc(PSF License 2.0), 26 classes,IterableandSizedvirtual.Comparison with the old allocator
Same test binary, same orders, built against the previous
initialize.hpp. Total v-table size:A1; B1, C1 : virtual A; D1 : B, CE3 : CA1, B1, C1 : A, B, E1 : BTrees and the other small cases are unchanged. Two honest caveats, recorded in the test comments: on socketserver and on the Django views the old allocator's single best order beats the new total by 1 and 5 entries respectively. The greedy is not an optimum; what it buys is a total that no longer depends on registration order, and that is far below the old allocator's typical and worst cases.
🤖 Generated with Claude Code
https://claude.ai/code/session_012QA4PCHE4oooe1ZtG9aKj7