perf: P9 - TakeOut/PutBack on the map #56

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

Context

F10: the member that reaches a map entry. Waits until there are two back ends to answer to.

Source

Acceptance criteria

  • The round as PERFORMANCE.md section 5 specifies it
  • The named benchmark or snapshot recorded before and after

Dependencies

None known.

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

## Context F10: the member that reaches a map entry. Waits until there are two back ends to answer to. ## Source - [PERFORMANCE.md round P9](https://git.torb.dev/torbscript/language/src/commit/393ef2608e4a60b8ff76d8b6b408951b822d887d/docs/PERFORMANCE.md#L1219) ## Acceptance criteria - [ ] The round as PERFORMANCE.md section 5 specifies it - [ ] The named benchmark or snapshot recorded before and after ## Dependencies None known. _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 P9, not a duplicate. The record says it waits until there are two back ends to answer to; refine checks that against the current state (the VM exists) and measures in forge CI. next: refine, waiting (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 P9, not a duplicate. The record says it waits until there are two back ends to answer to; refine checks that against the current state (the VM exists) and measures in forge CI. next: refine, waiting (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-23T04:31:18Z

[loop:torbscript] claim refine by session:loop-torbscript-20261023 until 2026-10-23T04:31:18Z
Author
Collaborator

[loop:torbscript] decided by the loop: P9 is unblocked and is cut to its core; the member rework follows as its own step.

  • Precondition holds. The bytecode back end and the VM exist on current main (compiler/src/backend/bytecode, compiler/src/vm; VM.md is "implemented", milestone 7). The same holds for #58 (P11) and #59 (P12), which carry the same "after the VM" gate; P9 does not depend on either.
  • State on main. Instruction.TakeOut/PutBack with RuntimeKind.Map exist in the IR and nothing reaches them; torb_map_take_out/torb_map_put_back exist in runtime/map.c but there is no torb_map_slot; the bytecode emitter rejects both instructions ("a var path through a map"); place.trb lowers a map path as a get plus a set (two probes, value copied both ways); Map.update/getOrInsert in std/collections/src/map.trb are written as get + set.
  • Baseline (recorded in PERFORMANCE.md, F10 and the P7 table): benchmarks/map-count 5.78x against the C reference, 50 048 allocations, the key hashed twice per word.
  • Cut. Size is more than one change in one go (runtime, IR pass, two back ends, std). This issue is the core: map write-through as one probe. The getOrInsert/update rework on top of it is the next step and is filed just in time once this one has landed (after a keyword duplicate search), not now.
[loop:torbscript] decided by the loop: P9 is unblocked and is cut to its core; the member rework follows as its own step. - **Precondition holds.** The bytecode back end and the VM exist on current main (`compiler/src/backend/bytecode`, `compiler/src/vm`; VM.md is "implemented", milestone 7). The same holds for #58 (P11) and #59 (P12), which carry the same "after the VM" gate; P9 does not depend on either. - **State on main.** `Instruction.TakeOut`/`PutBack` with `RuntimeKind.Map` exist in the IR and nothing reaches them; `torb_map_take_out`/`torb_map_put_back` exist in `runtime/map.c` but there is no `torb_map_slot`; the bytecode emitter rejects both instructions ("a `var` path through a map"); `place.trb` lowers a map path as a `get` plus a `set` (two probes, value copied both ways); `Map.update`/`getOrInsert` in `std/collections/src/map.trb` are written as `get` + `set`. - **Baseline (recorded in PERFORMANCE.md, F10 and the P7 table):** `benchmarks/map-count` 5.78x against the C reference, 50 048 allocations, the key hashed twice per word. - **Cut.** Size is more than one change in one go (runtime, IR pass, two back ends, std). This issue is the core: map write-through as one probe. The `getOrInsert`/`update` rework on top of it is the next step and is filed just in time once this one has landed (after a keyword duplicate search), not now.
Author
Collaborator

[loop:torbscript] refine: ready.

Goal. A read-modify-write and a write through map[key] on a concrete map probe the table once: the pair of get/set (and the take-out/put-back of two members) becomes one TakeOut and one PutBack with RuntimeKind.Map, on an interior pointer from a new torb_map_slot, in both back ends (PERFORMANCE.md F10 / round P9, "the fix", first half).

Acceptance criteria

  • runtime/map.c gains torb_map_slot(map, key): a pointer to the value slot, valid until the next write to the table; a missing key is the insert/panic the current MutableIndex.set/Index.at pair produces, with the same message.
  • The IR pass (extension of ir/elements.trb or a sibling, plus ir/lower/place.trb) turns the round trip on a concrete map into one takeOut and one putBack; ir/verify.trb accepts them. A new IR snapshot test shows one takeOut/putBack where main shows two callWitness.
  • The C back end emits the slot access; the bytecode emitter and the VM handle TakeOut/PutBack on a map (the two report sites in backend/bytecode/emit.trb are gone, a kernel operation carries it).
  • The conformance suite gives the same output, exit code, panics and leak count on both back ends, including: missing key, nested map[a][b].f = v, a value that is a counted type (copy on write after makeUnique), and a write inside the access that would rehash (rejected by exclusivity, test pins it).
  • Measurement, which decides: benchmarks/map-count run in forge CI on the PR's base and head with benchmarks/run.sh (same runner, RUNS=9, fastest of them). Passes if the ratio against C drops from the 5.78x baseline by at least 20% on the same runner, and allocations do not exceed 50 048. If the measured gain is below that, the PR documents the numbers and the loop decides then (keep, because the copy goes away, or drop); no owner question either way.
  • PERFORMANCE.md: P9 marked done (status line, table row, F10 with the measured before/after), skills/ regenerated.

Design record. docs/PERFORMANCE.md F10 and round P9 (section 5), docs/design/VM.md for the kernel operation. The "after the VM" condition is met (see above).

Seed impact. No syntax change and no std name the compiler looks up by string. A new native-level entry (torb_map_slot and a VM kernel operation) is involved, so the two-commit rule applies: commit 1 perf(runtime): torb_map_slot and the map take-out in the VM (runtime + bytecode emitter + VM, nothing reaches it yet, tests at IR level), commit 2 perf(ir): a map read-modify-write is one probe (pass, C emission, snapshot, benchmark, docs). The seed refresh itself is not part of this and stays with the owner.

Out of scope (next step, just in time): Map.getOrInsert/Map.update written on top of the slot.

Tokens for this refine run: roughly 60k (read-only repo check, no CI run needed before ready since the baseline is already recorded; CI measurement is the PR's criterion).

[loop:torbscript] refine: ready. **Goal.** A read-modify-write and a write through `map[key]` on a concrete map probe the table once: the pair of `get`/`set` (and the take-out/put-back of two members) becomes one `TakeOut` and one `PutBack` with `RuntimeKind.Map`, on an interior pointer from a new `torb_map_slot`, in both back ends (PERFORMANCE.md F10 / round P9, "the fix", first half). **Acceptance criteria** - [ ] `runtime/map.c` gains `torb_map_slot(map, key)`: a pointer to the value slot, valid until the next write to the table; a missing key is the insert/panic the current `MutableIndex.set`/`Index.at` pair produces, with the same message. - [ ] The IR pass (extension of `ir/elements.trb` or a sibling, plus `ir/lower/place.trb`) turns the round trip on a concrete map into one `takeOut` and one `putBack`; `ir/verify.trb` accepts them. A new IR snapshot test shows one `takeOut`/`putBack` where `main` shows two `callWitness`. - [ ] The C back end emits the slot access; the bytecode emitter and the VM handle `TakeOut`/`PutBack` on a map (the two `report` sites in `backend/bytecode/emit.trb` are gone, a kernel operation carries it). - [ ] The conformance suite gives the same output, exit code, panics and leak count on both back ends, including: missing key, nested `map[a][b].f = v`, a value that is a counted type (copy on write after `makeUnique`), and a write inside the access that would rehash (rejected by exclusivity, test pins it). - [ ] Measurement, which decides: `benchmarks/map-count` run in forge CI on the PR's base and head with `benchmarks/run.sh` (same runner, RUNS=9, fastest of them). Passes if the ratio against C drops from the 5.78x baseline by at least 20% on the same runner, and allocations do not exceed 50 048. If the measured gain is below that, the PR documents the numbers and the loop decides then (keep, because the copy goes away, or drop); no owner question either way. - [ ] PERFORMANCE.md: P9 marked done (status line, table row, F10 with the measured before/after), `skills/` regenerated. **Design record.** `docs/PERFORMANCE.md` F10 and round P9 (section 5), `docs/design/VM.md` for the kernel operation. The "after the VM" condition is met (see above). **Seed impact.** No syntax change and no std name the compiler looks up by string. A new native-level entry (`torb_map_slot` and a VM kernel operation) is involved, so the two-commit rule applies: commit 1 `perf(runtime): torb_map_slot and the map take-out in the VM` (runtime + bytecode emitter + VM, nothing reaches it yet, tests at IR level), commit 2 `perf(ir): a map read-modify-write is one probe` (pass, C emission, snapshot, benchmark, docs). The seed refresh itself is not part of this and stays with the owner. **Out of scope (next step, just in time):** `Map.getOrInsert`/`Map.update` written on top of the slot. Tokens for this refine run: roughly 60k (read-only repo check, no CI run needed before ready since the baseline is already recorded; CI measurement is the PR's criterion).
Author
Collaborator

[loop:torbscript] claim implement by session:loop-torbscript-20261023 until 2026-10-23T08:41:20Z

[loop:torbscript] claim implement by session:loop-torbscript-20261023 until 2026-10-23T08:41:20Z
argus closed this issue 2026-10-23 07:57:41 +00:00
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#56
No description provided.