Skip to content

perf(gc): right-size per-object footprint — INLINE_SLOT_FLOOR=4 reserves 4 slots for every object (#7469 Workstream B) #7714

Closed

Description

@proggeramlug

Workstream B — the per-object footprint half of #7469, which was quantified but never implemented.

Workstream A is done and closed (#7565/#7566: TLS resolution count + the loop-site inline-bump gate, churn_alloc 1.81× at +0 bytes for non-loop sites), which exceeded its own ≥2× acceptance bar. B never started, and #7469 is being closed so its remaining scope stops hiding behind a finished umbrella.

What is actually left

crates/perry-runtime/src/object/mod.rs:24 still reads:

pub(crate) const INLINE_SLOT_FLOOR: usize = 4;

unchanged since the measurement. Every object allocation therefore reserves at least 4 slots regardless of the shape's real field count — js_object_alloc(0, 0) (the {} and FFI path) allocates 4 slots for zero fields.

The measurement to start from, and its ceiling

#7469's own footprint work measured 136.9 B/record = 84.4 B arena + 52.6 B OUTSIDE the arena. That out-of-arena half is the important number: it caps any purely-arena remedy at ~113 B/record, not the ~80 B a naive reading of the arena figure suggests. Anyone picking this up should re-derive both halves before designing, because a right-sizing change that only moves the 84.4 B cannot reach the target.

Two warnings from the last attempt

Bench host per the standing rule: the quiet M1 mini, not a dev Mac.

Activity

  1. added
    performanceRuntime, compile-time, build-size, or memory performance
    on Aug 9, 2026
  2. proggeramlug commented on Aug 10, 2026

    @proggeramlug
    ContributorAuthor

    Re-profiled before coding, as this issue instructs — and the headline number is much smaller than a slot count suggests.

    The A/B

    Same tree, one constant pair moved in lockstep (INLINE_SLOT_FLOOR 4→2 and its codegen mirror MIN_FIELD_SLOTS in lower_call/new_alloc.rs), perry-dev, archives rebuilt and mtime-verified, M1 Max laptop:

    bench floor=4 floor=2 delta
    retain (3M × {a,b} — 2 fields, the best case for this change) 317.6 MB 308.9 MB −8.7 MB (−2.7%)
    retain_wide (≥4 fields, floor never binds) 445.7 MB 445.6 MB −0.1 MB (0.0%)
    churn_alloc 24.7 MB 24.7 MB 0.0

    Output byte-identical on all three, and cargo test -p perry-runtime --lib is 2052 passed / 0 failed at floor=2 — so the corruption-critical invariant does hold when both constants move together, which is worth knowing before anyone designs the real change.

    Why that is the interesting part

    A slot calculation predicts far more. retain allocates 3,000,000 two-field records; at 8 bytes per slot, dropping two reserved slots should return 16 B/record ≈ 48 MB. Measured: 8.7 MB, i.e. 2.9 B/record — about 18% of the theoretical figure.

    The rest is absorbed before it reaches RSS: allocation lands in size classes, so a 2-slot and a 4-slot object of this shape frequently occupy the same bucket, and shrinking the reservation does not shrink the bucket. That is the same shape as this issue's own warning about the 52.6 B/record that lives outside the arena capping any arena-only remedy — the ceiling is lower again than that warning implies, because part of the arena half is not recoverable either.

    What this means for the workstream

    Right-sizing the floor is not, on this evidence, a footprint lever worth a corruption-critical change on its own: 2.7% on the single most favourable workload, 0% on the two others, against a constant whose comment says allocation, every bounds check and every direct-slot read must move in lockstep — plus a gc_ratchet re-pin, since the footprint cells move.

    If Workstream B is worth continuing, the measurement says to aim at the size-class boundary rather than at the reservation: an allocation that returns a smaller bucket is where the 48 MB actually is. I have not implemented that; it is a different change from the one this issue describes, and it should be scoped from a fresh profile of the allocator's size classes rather than from INLINE_SLOT_FLOOR.

    Leaving this open, rescoped by the numbers above rather than closed, since the underlying goal (per-object footprint) is real — only the named lever is weaker than it looks.

  3. proggeramlug commented on Aug 12, 2026

    @proggeramlug
    ContributorAuthor

    Fixed by merged PR #7928. Current origin/main has INLINE_SLOT_FLOOR = 2 in both the runtime and codegen target layout, with equality guards, exact allocation-footprint coverage, overflow-growth behavior coverage, and the requested corpus/GC validation recorded in the PR and changelog.d/7928-inline-slot-floor.md.

    The separate residual work is already scoped correctly elsewhere: #7929 covers the byte-budget/object-cost GC interaction discovered by this change, while #7916 retains the broader object-header footprint work. This ticket's named Workstream B floor change is complete.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    performanceRuntime, compile-time, build-size, or memory performance

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions