perf: P12 - F13 again on the code P5 to P7 leave behind #59

Closed
opened 2026-10-05 21:07:15 +00:00 by orion · 9 comments
Collaborator

Context

PERFORMANCE.md finding F13 measured that making torb_retain, torb_release and torb_make_unique static inline in runtime/include/torb.h was worth up to 14% and cost up to 9%, depending on the loop, and left it "a measurement to redo once findings 2 to 5 have removed" the indirect calls and copies. Rounds P5 to P7 landed (section 5: done), and the bytecode back end and the VM exist, so the condition of the round table ("after the VM", "two back ends to answer to") is met. This round redoes the measurement on the code they leave behind and keeps the change only if the numbers say so.

Source

  • PERFORMANCE.md round P12 (section 5 row P12), finding F13
  • runtime/memory.c (torb_retain, torb_release, torb_make_unique), runtime/include/torb.h

Goal

Measure the three fast paths inline against the runtime as it is, on the benchmark table of today's main, and land the variant (if any) that wins. The fast paths have grown since F13 was written (immortal blocks, the shared-count bit and torb_pool_threaded, the release depth bound), so "inline" means the header holds the common case and the rest stays a call.

Acceptance criteria

  • Variants, each a header-only change over main's memory.c semantics: (A) as is; (B) torb_retain inline; (C) torb_retain and the unique test of torb_make_unique inline; (D) all three. Inline code holds only the common case (non-NULL, not immortal, not shared/threaded for retain; count above one and not shared/threaded for release); every other case calls the out-of-line function, so panics, the release depth bound, the atomic path and the immortal rule stay in memory.c unchanged.
  • Measurement in the forge CI on one runner, base and every variant in the same job: benchmarks/run.sh --allocations with RUNS=9, fastest of nine. A temporary measurement job on the PR branch is fine and is removed before the last commit; no new gate. The full table (all rows of section 4 that exist in benchmarks/) goes into the PR description, and the rows list-iterate, pipeline, record-write, list-index, map-count, binary-trees (game) are named.
  • Decision rule, decided by the loop and recorded in the PR: a variant lands if it is at least 5% faster on at least two of the named rows, no named row is more than 3% slower, and the C and the VM both still pass the conformance suite. Allocations do not change in any row (they are exact). If no variant meets the rule, the PR changes docs/PERFORMANCE.md only, with the numbers; that is a valid outcome of the round, not a failure.
  • If a variant lands: it is the whole code change (runtime/include/torb.h, runtime/memory.c where a function becomes the slow path), the runtime tests (runtime/tests) are green in both CI tiers, the VM's kernel (machine.c) still reaches the same out-of-line entry points, and an -O2 object size delta of the emitted program.c of list-iterate is recorded (code size is the cost of inlining).
  • docs/PERFORMANCE.md: F13 gets the new table and the verdict; section 5 row P12 is marked done and the status line at the top no longer lists P12 as waiting; the stale "after the VM" wording of the row is corrected; torb docs check docs, torb docs index --check docs and the skills/ regeneration are green.

Design record point

PERFORMANCE.md F13 and round P12 (section 5). BACKEND 6.3 (-flto is unusable with this toolchain, which is why the fast paths are not inlined across units today; if -flto works on the CI runner's gcc now, record it in F13 and do not make it part of this round).

Seed impact

None expected: a header-only change to the runtime, no syntax, no native, no name looked up by string. The seed is not touched.

Size

One PR. One commit if the PR only records the numbers; two Conventional Commits if a variant lands (perf(runtime): inline the fast path of torb_retain ... and docs(performance): redo finding 13 on the code P5 to P7 leave behind).

Out of scope

P9 (#56) and P11 (#58, #415): their passes change the IR, not these functions, and this round measures main as it stands when the PR runs (re-measure if one lands while the PR is open). Atomic reference counts or the pool's threaded mode. Any change to the C that the back end emits. -flto. The seed.

Dependencies

None.

Moved from the planning lists of the docs; the design record stays the specification.

## Context PERFORMANCE.md finding F13 measured that making `torb_retain`, `torb_release` and `torb_make_unique` `static inline` in `runtime/include/torb.h` was worth up to 14% and cost up to 9%, depending on the loop, and left it "a measurement to redo once findings 2 to 5 have removed" the indirect calls and copies. Rounds P5 to P7 landed (section 5: done), and the bytecode back end and the VM exist, so the condition of the round table ("after the VM", "two back ends to answer to") is met. This round redoes the measurement on the code they leave behind and keeps the change only if the numbers say so. ## Source - [PERFORMANCE.md round P12](https://git.torb.dev/torbscript/language/src/commit/393ef2608e4a60b8ff76d8b6b408951b822d887d/docs/PERFORMANCE.md#L1222) (section 5 row P12), finding F13 - `runtime/memory.c` (`torb_retain`, `torb_release`, `torb_make_unique`), `runtime/include/torb.h` ## Goal Measure the three fast paths inline against the runtime as it is, on the benchmark table of today's `main`, and land the variant (if any) that wins. The fast paths have grown since F13 was written (immortal blocks, the shared-count bit and `torb_pool_threaded`, the release depth bound), so "inline" means the header holds the common case and the rest stays a call. ## Acceptance criteria - [ ] Variants, each a header-only change over `main`'s `memory.c` semantics: (A) as is; (B) `torb_retain` inline; (C) `torb_retain` and the unique test of `torb_make_unique` inline; (D) all three. Inline code holds only the common case (non-NULL, not immortal, not shared/threaded for retain; count above one and not shared/threaded for release); every other case calls the out-of-line function, so panics, the release depth bound, the atomic path and the immortal rule stay in `memory.c` unchanged. - [ ] Measurement in the forge CI on one runner, base and every variant in the same job: `benchmarks/run.sh --allocations` with `RUNS=9`, fastest of nine. A temporary measurement job on the PR branch is fine and is removed before the last commit; no new gate. The full table (all rows of section 4 that exist in `benchmarks/`) goes into the PR description, and the rows `list-iterate`, `pipeline`, `record-write`, `list-index`, `map-count`, `binary-trees` (game) are named. - [ ] Decision rule, decided by the loop and recorded in the PR: a variant lands if it is at least 5% faster on at least two of the named rows, no named row is more than 3% slower, and the C and the VM both still pass the conformance suite. Allocations do not change in any row (they are exact). If no variant meets the rule, the PR changes `docs/PERFORMANCE.md` only, with the numbers; that is a valid outcome of the round, not a failure. - [ ] If a variant lands: it is the whole code change (`runtime/include/torb.h`, `runtime/memory.c` where a function becomes the slow path), the runtime tests (`runtime/tests`) are green in both CI tiers, the VM's kernel (`machine.c`) still reaches the same out-of-line entry points, and an `-O2` object size delta of the emitted `program.c` of `list-iterate` is recorded (code size is the cost of inlining). - [ ] `docs/PERFORMANCE.md`: F13 gets the new table and the verdict; section 5 row P12 is marked done and the status line at the top no longer lists P12 as waiting; the stale "after the VM" wording of the row is corrected; `torb docs check docs`, `torb docs index --check docs` and the `skills/` regeneration are green. ## Design record point PERFORMANCE.md F13 and round P12 (section 5). BACKEND 6.3 (`-flto` is unusable with this toolchain, which is why the fast paths are not inlined across units today; if `-flto` works on the CI runner's gcc now, record it in F13 and do not make it part of this round). ## Seed impact None expected: a header-only change to the runtime, no syntax, no native, no name looked up by string. The seed is not touched. ## Size One PR. One commit if the PR only records the numbers; two Conventional Commits if a variant lands (`perf(runtime): inline the fast path of torb_retain ...` and `docs(performance): redo finding 13 on the code P5 to P7 leave behind`). ## Out of scope P9 (#56) and P11 (#58, #415): their passes change the IR, not these functions, and this round measures `main` as it stands when the PR runs (re-measure if one lands while the PR is open). Atomic reference counts or the pool's threaded mode. Any change to the C that the back end emits. `-flto`. The seed. ## Dependencies None. _Moved from the planning lists of the docs; the design record stays the specification._
orion added this to the Kernel of natives milestone 2026-10-05 21:07:15 +00:00
Author
Collaborator

[loop:torbscript] intake: in scope (author orion, label loop), source PERFORMANCE.md round P12, not a duplicate. Follows the rounds P5 to P7; refine checks whether they landed. next: waiting (rounds P5 to P7 and ready buffer full: #4, #163, #164, #223 are ready) until 2026-10-10T04:00:00Z

[loop:torbscript] intake: in scope (author orion, label loop), source PERFORMANCE.md round P12, not a duplicate. Follows the rounds P5 to P7; refine checks whether they landed. next: waiting (rounds P5 to P7 and ready buffer full: #4, #163, #164, #223 are ready) until 2026-10-10T04:00:00Z
Author
Collaborator

[loop:torbscript] claim refine by session:loop-torbscript-20261023 until 2026-10-23T08:16:00Z

[loop:torbscript] claim refine by session:loop-torbscript-20261023 until 2026-10-23T08:16:00Z
Author
Collaborator

[loop:torbscript] refine: ready, next: implement (one PR, oldest-first once WIP is free).

Findings.

  • Precondition "two back ends": met. The bytecode VM and the C back end share the IR and the conformance suite (same basis as #56 and #58). P5 to P7 are done in section 5, so F13's own condition (measure again once the indirect calls and copies are gone) holds.
  • Not a duplicate: keyword search over open and closed issues (F13, torb.h, torb_retain, inline, P12) finds only the sibling rounds #56 (P9), #58 (P11, step #415) and finished perf items; none touches runtime/include/torb.h.
  • The runtime changed since F13 was measured: torb_retain/torb_release now also handle the immortal count, the shared-count bit with torb_pool_threaded, and the release depth bound. So "inline all three" can no longer be a copy of the old experiment; only the common case can be inline.

Decided by the loop (performance, measurable, internal; no owner question).

  • Four variants (as is, retain, retain + unique test, all three), measured in one forge CI job with benchmarks/run.sh --allocations, RUNS=9, fastest of nine.
  • Rule, from F13's own spread (up to -14% and +9%): a variant lands if at least 5% faster on two or more of the named rows, none of them more than 3% slower, conformance green on both back ends. Otherwise the PR is docs only with the numbers, which is a valid outcome.
  • Slow paths stay out of line in memory.c, so panics, the atomic path, the depth bound and the immortal rule do not change.

Size. One PR: one commit (numbers only) or two (inline change + PERFORMANCE.md). Header and memory.c only. Not an epic, so no split.

Seed impact. None expected (header-only runtime change, no syntax, native or looked-up name). The issue body now holds the sharpened goal, criteria, scope, out-of-scope and size.

Tokens for this refine run: roughly 45k (read-only repo check).

[loop:torbscript] refine: ready, next: implement (one PR, oldest-first once WIP is free). **Findings.** - Precondition "two back ends": met. The bytecode VM and the C back end share the IR and the conformance suite (same basis as #56 and #58). P5 to P7 are done in section 5, so F13's own condition (measure again once the indirect calls and copies are gone) holds. - Not a duplicate: keyword search over open and closed issues (F13, torb.h, torb_retain, inline, P12) finds only the sibling rounds #56 (P9), #58 (P11, step #415) and finished perf items; none touches `runtime/include/torb.h`. - The runtime changed since F13 was measured: `torb_retain`/`torb_release` now also handle the immortal count, the shared-count bit with `torb_pool_threaded`, and the release depth bound. So "inline all three" can no longer be a copy of the old experiment; only the common case can be inline. **Decided by the loop (performance, measurable, internal; no owner question).** - Four variants (as is, retain, retain + unique test, all three), measured in one forge CI job with `benchmarks/run.sh --allocations`, RUNS=9, fastest of nine. - Rule, from F13's own spread (up to -14% and +9%): a variant lands if at least 5% faster on two or more of the named rows, none of them more than 3% slower, conformance green on both back ends. Otherwise the PR is docs only with the numbers, which is a valid outcome. - Slow paths stay out of line in `memory.c`, so panics, the atomic path, the depth bound and the immortal rule do not change. **Size.** One PR: one commit (numbers only) or two (inline change + PERFORMANCE.md). Header and `memory.c` only. Not an epic, so no split. **Seed impact.** None expected (header-only runtime change, no syntax, native or looked-up name). The issue body now holds the sharpened goal, criteria, scope, out-of-scope and size. Tokens for this refine run: roughly 45k (read-only repo check).
Author
Collaborator

[loop:torbscript] claim implement by session:loop-torbscript-20261023 until 2026-10-23T12:26:00Z

[loop:torbscript] claim implement by session:loop-torbscript-20261023 until 2026-10-23T12:26:00Z
Author
Collaborator

[loop:torbscript] release implement: lease expired 2026-10-23T12:26Z with no live worker and no result comment; branch loop/59-inline-refcount-fastpath holds 3 temporary measurement commits (last 08:20Z), no PR yet. next: fresh implement worker continues from that branch

[loop:torbscript] release implement: lease expired 2026-10-23T12:26Z with no live worker and no result comment; branch loop/59-inline-refcount-fastpath holds 3 temporary measurement commits (last 08:20Z), no PR yet. next: fresh implement worker continues from that branch
Author
Collaborator

[loop:torbscript] claim implement by session:loop-torbscript-20261023 until 2026-10-23T18:30Z

[loop:torbscript] claim implement by session:loop-torbscript-20261023 until 2026-10-23T18:30Z
Author
Collaborator

[loop:torbscript] release implement: lease expired 2026-10-23T18:30Z with no live worker (dispatch.liveWorkers 0) and no result comment; branch loop/59-inline-refcount-fastpath kept. next: fresh implement worker

[loop:torbscript] release implement: lease expired 2026-10-23T18:30Z with no live worker (dispatch.liveWorkers 0) and no result comment; branch loop/59-inline-refcount-fastpath kept. next: fresh implement worker
Author
Collaborator

[loop:torbscript] claim implement by session:loop-torbscript-20261024 until 2026-10-24T04:30Z

[loop:torbscript] claim implement by session:loop-torbscript-20261024 until 2026-10-24T04:30Z
argus closed this issue 2026-10-23 22:35:06 +00:00
Author
Collaborator

[loop:torbscript] implement: variant D landed. The measurement job from the earlier branch (one runner, RUNS=9, passes A B C D and D C B A) gave list-iterate -26.0%, pipeline -23.8%, map-count -8.9%, binary-trees -8.3% with the three fast paths inline; slowest named rows list-index +2.4%, record-write +1.9% (B and C failed the 3% rule on record-write). Allocations identical in every row. Measurement scaffolding is not in the PR; the history is three commits (fix(benchmarks) so --allocations links runtime/os, perf(runtime), docs(performance) with the full table, verdict, code-size delta and the P12 row done). PR #444, forge CI green on all required jobs, approved and merged by argus (rebase), so the review stage is done; next: close (main CI on the landed commit 7d4fe624). Claim by session:loop-torbscript-20261024 released. Tokens for this run: roughly 180k (estimate).

[loop:torbscript] implement: variant D landed. The measurement job from the earlier branch (one runner, RUNS=9, passes A B C D and D C B A) gave list-iterate -26.0%, pipeline -23.8%, map-count -8.9%, binary-trees -8.3% with the three fast paths inline; slowest named rows list-index +2.4%, record-write +1.9% (B and C failed the 3% rule on record-write). Allocations identical in every row. Measurement scaffolding is not in the PR; the history is three commits (fix(benchmarks) so --allocations links runtime/os, perf(runtime), docs(performance) with the full table, verdict, code-size delta and the P12 row done). PR https://git.torb.dev/torbscript/language/pulls/444, forge CI green on all required jobs, approved and merged by argus (rebase), so the review stage is done; next: close (main CI on the landed commit 7d4fe624). Claim by session:loop-torbscript-20261024 released. Tokens for this run: roughly 180k (estimate).
Sign in to join this conversation.
No labels
loop
ready
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
torbscript/language#59
No description provided.