Skip to content

perf(deque,queue,priority_queue): annotate consuming parameters with #owned - #3982

Merged
bobzhang merged 1 commit into
mainfrom
Yu-zh/rc-annotation-deque
Aug 7, 2026
Merged

perf(deque,queue,priority_queue): annotate consuming parameters with #owned#3982
bobzhang merged 1 commit into
mainfrom
Yu-zh/rc-annotation-deque

Conversation

@Yu-zh

@Yu-zh Yu-zh commented Aug 6, 2026

Copy link
Copy Markdown
Collaborator

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 into self.buf unconditionally
  • Queue::push — forwards into the now-owned Deque::push_back
  • PriorityQueue: push, plus the private meld/merges node plumbing (every branch either stores the node into the other node or returns it)

Why

#owned transfers 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 String elements 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.

bench session 1 (10 rounds) session 2 (8 rounds)
Deque::push_back −5.2% −3.4%
PriorityQueue::push −7.7% −9.9%
PriorityQueue push+drain −11.4% −12.3%

Control 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-dominated List::prepend control 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 s read from another container, RC traffic drops from 3 ops to 1:

// before                          // after
moonbit_incref(s);                 moonbit_incref(s);   // ownership moves into the call
Deque_push_back(dst, s);           Deque_push_back(dst, s);
moonbit_decref(s);                 // gone — callee consumed the reference

// inside push_back:               // inside push_back:
moonbit_incref(value);             // gone — the store consumes the owned ref
buf[write_idx] = value;            buf[write_idx] = value;

Static RC-op counts in the emitted function bodies: Deque::push_back store-side incref removed; PriorityQueue::push 5 → 2; meld 14 → 10; merges 17 → 12.

…#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.
@Yu-zh
Yu-zh requested a review from Guest0x0 August 6, 2026 06:59
@bobzhang

bobzhang commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

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), Queue::push forwards into Deque::push_back annotated in this same PR (clean ownership chain, no boundary incref), and priority_queue's meld/merges/push sink their nodes into the returned structure on every branch.

Verified locally on the combined state of all six #owned PRs (3982–3988) merged onto current main: moon check clean, moon fmt/moon info zero drift, wasm-gc 7021/7021, native 6931/6931. CI green (9/9).

One sequencing note: #3969 (deque wrap_index rewrite) touches the same deque.mbt functions and will need a rebase once this lands.

🤖 Generated with Claude Code

@bobzhang
bobzhang merged commit ff4b191 into main Aug 7, 2026
15 checks passed
@bobzhang
bobzhang deleted the Yu-zh/rc-annotation-deque branch August 7, 2026 04:14
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.

2 participants