vm: retain and release of a one-pointer value as an instruction #164
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Goal
Make the retain and release of a value whose shape is one counted pointer an instruction of its own in the VM, instead of a call into the C kernel, the third open speed step of VM.md "Speed, the next steps".
Source
Closeorder, live-block counter).runtime/machine.ctorb_machine_release_blockandtorb_machine_retain_group: the semantics to keep (immortal count, shared count with atomics, a zero count is an internal error, a shape with a destructor queues the block with its last count in place).Split out of #8.
A premise of #8 that does not hold for this step
fibonacci(30)takes and returnsInt; it retains and releases nothing, so this step cannot show in that benchmark. It needs a workload that does retain: for example a record or closure passed and stored in a loop (benchmarks/closure.trbhas the shape; a counted-record twin may be needed). Which workload is part of the decision below.Acceptance criteria (once the mechanism is decided)
Boxed) is one instruction of the loop with no kernel call on its common path (count above one, not shared, not immortal); the other cases fall back to the kernel exactly as today.tools/conformance.sh --vm,leakgate, tier A and B green.Seed
Depends on the mechanism: a new
Machinenative (an inline retain/release intorb.h) is a seed-refresh change; reading and writing the count word with the existingMachine.load/Machine.storeis not, but ties the interpreter to the runtime's block header layout.Dependencies
Owner decision (comment below).
[loop:torbscript] owner decision: how does the VM retain and release a one-pointer value without a kernel call?
What. A retain or release is
Machine.operateintoruntime/machine.c(torb_machine_retain_value,torb_machine_release_block) even when the shape is one counted pointer. To make it an instruction of the loop, something has to touch the block's count: an unshared block iscount > 1decrement or+1, but a shared block (several workers, atomic), an immortal one (0xFFFFFFFF), a count of zero (internal error) and a shape with a destructor (queue the block) must fall to the kernel as they do today.Options.
Machine.retainBlock(address)/ a fastreleaseBlockreturning "not handled" for the slow cases),static inlineintorb.hover the runtime's real header and atomics. Correct by construction, no layout duplicated in TorbScript. New native: two commits and a seed refresh, which the loop may not run.Machine.load/storeon the count word ininterpret.trb: no seed, but the interpreter hard-codes the header layout (32-bit count in a 64-bit word, shared flag, immortal sentinel, little-endian), and any change totorb_headersilently breaks it. Needs a test that pins the layout.fibonacci(30)retains nothing, so the step is invisible there; a retaining workload (closure or counted-record loop) has to show a worthwhile win before a native or a duplicated layout is justified.Recommendation. Option 3 first, then option 1 if the number justifies it: #165 (or this issue's first commit) adds the retaining workload to the measurement; if retain/release is under, say, 10 % of that run, drop the step. If it is large, option 1 over option 2 (layout stays in C). Say "go option 1/2/3".
Question q1 for torben (decision, blocking): How does the VM retain and release a one-pointer value without a kernel call?
[loop:torbscript] refine: owner decision filed (q1), next: waiting (owner decision q1)
[loop:torbscript] owner decision recorded (torben via WhatsApp, 2026-10-08 16:35 Europe/Berlin): "Okay, mess das. Frag so was nicht bevor du es nicht gemessen hast, so was kannst und sollst du proaktiv messen" (measure it; do not ask such things before measuring, measure proactively).
Resolution:
measure-first. Scope: build a retaining workload benchmark, measure the kernel-call cost in CI (PERFORMANCE.md 9.5 method). If there is a clear win, pick the option on the merits yourself (inline-nativespreferred, it is correct by construction; a seed refresh is fine); if not, record the numbers in PERFORMANCE.md and close as not taken. No further owner question needed. Labelledready.Standing guidance from the same answer: "Das meiste davon konntest du selbst beantworten" (most of this you could have answered yourself). Decide performance steps, seed refreshes that follow an existing plan, naming that follows the conventions, and anything measurable on your own; measure before asking; large refactors are fine when they make sense, size is no argument against an option.
[question-closed] q=q1 reason=answered-elsewhere
[loop:torbscript] claim implement by session:loop-torbscript-20261009 until 2026-10-09T04:42Z
Windowandwindows(CONCURRENCY slice F, gap 6) #4Windowandwindows(CONCURRENCY slice F, gap 6) #4[loop:torbscript] implement: PR #239, CI success, next: review (waiting on the merge gate argus)
Measured outcome (forge CI, PERFORMANCE.md 9.5 method: alternating binaries, processor time, mean of three, ratio):
benchmarks/chain-walk.trb, new) runs 1.15x faster with retain/release counted in the interpreter loop, in every pair (run 406: 1.156; run 419: 1.152 for the TorbScript form, 1.162 for the native form).Machine.load/store) and the native form (Machine.countUp/countDown,static inlineintorb.h) are equally fast. I chose the natives because they keep the header layout in C and keep the sandbox memory-stop site exact. That is the preferredinline-nativesoption.conformance --vmpasses 279/279. The numbers and reasoning are in PERFORMANCE.md 9.11.Part 1 (PR #239, this lands now): the manifest entries, the
static inlinepair intorb.h, the regeneratedtorb_natives.h/machine_natives.c, five runtime tests,std/machinedeclarations, VM.md 6/10, the benchmark, and PERFORMANCE.md 9.11.Part 2 needs a seed refresh first, one that contains
Machine.countUp/countDown(two-commit rule; the refresh is the owner's). After it:torb-bytecode-1;.Kernelarm callscountUp/countDownand skips the tail when they return true; otherwise it falls back to the kernel call.e6468e2don branchloop/164-measure2(measured in run 419 with part 1's binary asTORB_SEED).Note: the first tier A run on the PR failed on two LSP diagnostics timing tests (
language-server.test.trb, unrelated to this change). They passed on the rerun.Token usage: ~0.39M tokens in this session since its last context summary (more before it).
--sandbox#49--sandbox#49Buffer<Item>as the heap storage primitive #54[loop:torbscript] implement: part 1 landed with PR #239. Part 2 (the interpreter arm using the counted-word offset, reference commit
e6468e2don branch loop/164-measure2) editscompiler/src/vm/interpret.trblike #163 part 2, which started now, and all worker slots are taken. next: waiting (#163 part 2 on the same file, no free worker slot) until 2026-10-09T10:45:00Ztorb#21login,logout,ownerandyankcommands #22torb audit#23torb vendor#24std/http#30torb doc --checkfail #31Decimalthat computes #34Float32arithmetic, comparison, text and narrowing fromFloat64(slice 1) #35setVariable#38Flags#47ArrayList, the hash tables and the tries onBuffer#55TakeOut/PutBackon the map #56forover a concrete list is a counted loop #58torb build --explain-copies#61Into<Path>signatures (PATH slice 2) #68Storage(URI gap 13) #69101 Switching Protocolsand a duplex connection (NETWORK slice 14) #74std/toml#77std/xml#78std/htmldocuments (epic) #79Dualnumbers (forward mode) #83std/ecswith oneStoreper group type, andstd/transform(ECS slice 1) #89std/htmlmarkup (WEB slice 1) #93[loop:torbscript] implement: #163 part 2 landed (PR #250), so the file is free, but part 2 of this issue cannot start yet: it needs a published seed that knows
Machine.countUp/Machine.countDownfrom part 1 (d43017cd, landed 2026-10-09T03:14Z). The newest published seed, torb-seed-7ee364b7a06d, is from the commit before part 1. Refreshing the seed is not the loop's job; the next nightly seed afterd43017cdunblocks it. next: waiting (published seed with the part 1 natives) until 2026-10-10T04:30:00Z[loop:torbscript] implement: unchanged, part 2 needs a published seed with the part 1 natives. next: waiting (published seed with the part 1 natives) until 2026-10-10T04:30:00Z
const r: RingList<Int> = [1, 2]) #212[loop:torbscript] claim implement by session:loop-torbscript-20261010 until 2026-10-10T15:40Z (unblocked: the published seed torb-seed-5f22f071d02f of nightly-20261010 postdates the part 1 commit)
[loop:torbscript] implement: PR #305 (head
b4a6ba91, CI success, argus requested), est. tokens 90000, next: waiting (review by argus)