perf(deque,queue,priority_queue): annotate consuming parameters with #owned - #3982
Conversation
…#owned Mark parameters whose reference is stored on every non-panic path as ownership-transferring, so last-use callers move the reference instead of paying an incref/decref pair per call: - Deque: push_back, push_front, insert, set - Queue: push (forwards into the owned Deque::push_back) - PriorityQueue: push, plus the meld/merges node plumbing Interleaved A/B benchmark (native, String elements): Deque::push_back -5~6%; generated C shows 3 RC ops per push reduced to 1.
Review sign-off (Claude)LGTM — sign-off. Every annotated parameter is consumed exactly once on each path: the four deque entry points store after their guard/realloc (which precede the consuming store), Verified locally on the combined state of all six #owned PRs (3982–3988) merged onto current main: One sequencing note: #3969 (deque wrap_index rewrite) touches the same 🤖 Generated with Claude Code |
What
Adds
#owned(...)to parameters whose reference is consumed (stored into the data structure, or returned) on every non-panic path — 8 annotations:Deque:push_back,push_front,insert,set— the value is stored intoself.bufunconditionallyQueue::push— forwards into the now-ownedDeque::push_backPriorityQueue:push, plus the privatemeld/mergesnode plumbing (every branch either stores the node into the other node or returns it)Why
#ownedtransfers ownership of one reference from caller to callee. For last-use call sites (the common pattern, e.g.q.push(compute())or pushing elements read out of another container), the reference moves into the store instead of paying an incref inside the callee plus a decref at the caller. Non-last-use callers incref before the call, which just relocates the incref the callee would have done — never worse.Benchmarks
Native target, 10k pre-built
Stringelements per op (so every store is an RC operation and value allocation is outside the timed region). Two binaries built from identical bench source, with and without this branch, run strictly interleaved; medians across rounds; two independent sessions.Deque::push_backPriorityQueue::pushPriorityQueuepush+drainControl benches over packages this branch does not touch (
Array::push,HashMap::set,Map::set,FixedArray::fill) stayed within ±1%, confirming the harness isolates the change. (An allocation-dominatedList::prependcontrol swings ±14% on identical code — run-to-run noise on this machine — so treat single-digit deltas with matching spread as indicative, not exact.)Generated code (native C)
Per push of an element
sread from another container, RC traffic drops from 3 ops to 1:Static RC-op counts in the emitted function bodies:
Deque::push_backstore-side incref removed;PriorityQueue::push5 → 2;meld14 → 10;merges17 → 12.