Skip to content

perf(codegen): #8084's callee rooting costs pipeline 3.9%, consuming #8157's win on that row #8159

Description

@proggeramlug

Summary

pipeline is the only corpus row that got slower across 2026-08-15's twenty merges: +0.28% instructions on main @ d6d7d0efe versus 83b6b8c69. Every other row is flat or faster, several enormously.

The number is small but it is 2× the measured noise floor (±0.15%, established by compiling main twice into separate cache/out dirs and confirming all 22 binaries byte-identical), and the run ranges do not overlap: base 3.7881–3.7936 G, main 3.8015–3.8053 G. Peak RSS is unchanged.

It is a give-back, not a shortfall

Building the intervening merge points attributes it to a single step:

arm pipeline instructions Δ
83b6b8c69 (session start) 3.7881 G
8b1b4b909 (#8157 merge) 3.6547 G −3.52%
53d63aad2 (#8084 merge) 3.8003 G +3.95%
d6d7d0efe (today's main) 3.7988 G −0.00%

#8157 delivered its full claimed −3.52% here. The next step gave all of it back and a little more.

Every other row moves by ≤0.03% across that same at8157 → at8084 step, so this is one localized change, not drift.

Attribution

The window is {#8158, #8084}. #8158 is a 7-line addition to BUILD_CACHE_ENV_VARS and cannot change emitted code, which leaves #8084 — specifically fix(codegen): root the callee across argument evaluation in three call arms, touching new_dynamic.rs, call_spread.rs and early_branches.rs. pipeline is spread- and call-heavy, so the mechanism fits: each rooted callee adds an open_rooted_group / adopt / reread / release sequence around a call site.

This is not a request to revert

#8084 fixes a real moving-GC rooting bug — a callee held in a bare register across argument evaluation is a pre-move address, and the dependency-scale corpus found 16 sites of that shape. Correctness wins over 3.9% on one row, and #8084 should stay.

Filing it because nobody wrote the cost down. A correctness fix that silently consumes an entire optimisation's win on a row is exactly the thing that later gets rediscovered as an unexplained regression, and #8157's pipeline win currently looks like it never happened.

Worth looking at

The rooting sequence is emitted per call site. Whether it can be narrowed — e.g. skipping the reread when nothing between adopt and use can allocate, which is a property the tree already computes elsewhere for loop polls (loop_purity::loop_may_allocate) — is the obvious question. That would keep the soundness property while paying for it only where a collection is actually reachable.

Measurement protocol: /usr/bin/time -l, min-of-5, arms interleaved round-robin with order reshuffled each round, per-arm PERRY_RUNTIME_DIR and PERRY_CACHE_DIR, PERRY_NO_AUTO_OPTIMIZE=1, all arms' libperry_runtime.a cmp-verified to differ, stdout sha256 identical across arms.

Refs #8084, #8157.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions