From 09821cf9cc311fdff513c568faf6fcb27e1e5c38 Mon Sep 17 00:00:00 2001 From: bQUARKz Date: Mon, 20 Apr 2026 08:45:34 +0100 Subject: [PATCH] implement PLN-0033 vm string sharing hot path --- crates/console/prometeu-bytecode/Cargo.toml | 2 +- .../prometeu-bytecode/src/program_image.rs | 4 +- crates/console/prometeu-bytecode/src/value.rs | 51 +++++++-- .../console/prometeu-hal/src/host_return.rs | 2 +- .../src/virtual_machine_runtime/dispatch.rs | 2 +- .../prometeu-vm/src/virtual_machine.rs | 19 ++- discussion/index.ndjson | 2 +- ...path-ownership-and-string-copy-pressure.md | 108 ++++++++++++++++++ ...ation-evidence-and-hot-path-measurement.md | 98 ++++++++++++++++ ...ng-for-materialization-vs-copy-pressure.md | 88 ++++++++++++++ 10 files changed, 359 insertions(+), 17 deletions(-) create mode 100644 discussion/workflow/plans/PLN-0033-vm-hot-path-ownership-and-string-copy-pressure.md create mode 100644 discussion/workflow/plans/PLN-0034-internal-allocation-evidence-and-hot-path-measurement.md create mode 100644 discussion/workflow/plans/PLN-0035-runtime-spec-wording-for-materialization-vs-copy-pressure.md diff --git a/crates/console/prometeu-bytecode/Cargo.toml b/crates/console/prometeu-bytecode/Cargo.toml index 8bc8be4a..9811f569 100644 --- a/crates/console/prometeu-bytecode/Cargo.toml +++ b/crates/console/prometeu-bytecode/Cargo.toml @@ -6,4 +6,4 @@ license.workspace = true repository.workspace = true [dependencies] -serde = { version = "1.0", features = ["derive"] } +serde = { version = "1.0", features = ["derive", "rc"] } diff --git a/crates/console/prometeu-bytecode/src/program_image.rs b/crates/console/prometeu-bytecode/src/program_image.rs index c3293dd8..9f1890ad 100644 --- a/crates/console/prometeu-bytecode/src/program_image.rs +++ b/crates/console/prometeu-bytecode/src/program_image.rs @@ -75,7 +75,7 @@ impl From for ProgramImage { ConstantPoolEntry::Int64(v) => Value::Int64(*v), ConstantPoolEntry::Float64(v) => Value::Float(*v), ConstantPoolEntry::Boolean(v) => Value::Boolean(*v), - ConstantPoolEntry::String(v) => Value::String(v.clone()), + ConstantPoolEntry::String(v) => Value::String(v.clone().into()), ConstantPoolEntry::Int32(v) => Value::Int32(*v), }) .collect(); @@ -99,7 +99,7 @@ impl From for BytecodeModule { Value::Int64(v) => ConstantPoolEntry::Int64(*v), Value::Float(v) => ConstantPoolEntry::Float64(*v), Value::Boolean(v) => ConstantPoolEntry::Boolean(*v), - Value::String(v) => ConstantPoolEntry::String(v.clone()), + Value::String(v) => ConstantPoolEntry::String(v.to_string()), Value::Int32(v) => ConstantPoolEntry::Int32(*v), Value::HeapRef(_) => ConstantPoolEntry::Null, }) diff --git a/crates/console/prometeu-bytecode/src/value.rs b/crates/console/prometeu-bytecode/src/value.rs index 495a761d..78b67bbe 100644 --- a/crates/console/prometeu-bytecode/src/value.rs +++ b/crates/console/prometeu-bytecode/src/value.rs @@ -1,5 +1,7 @@ use serde::{Deserialize, Serialize}; use std::cmp::Ordering; +use std::fmt::Write; +use std::sync::Arc; /// Opaque handle that references an object stored in the VM heap. /// @@ -26,7 +28,7 @@ pub enum Value { /// Boolean value (true/false). Boolean(bool), /// UTF-8 string. Strings are immutable and usually come from the Constant Pool. - String(String), + String(Arc), /// A handle to an object on the heap (opaque reference). HeapRef(HeapRef), /// Represents the absence of a value (equivalent to `null` or `undefined`). @@ -92,17 +94,46 @@ impl Value { } } + pub fn append_to_string(&self, out: &mut String) { + match self { + Value::Int32(i) => { + let _ = write!(out, "{}", i); + } + Value::Int64(i) => { + let _ = write!(out, "{}", i); + } + Value::Float(f) => { + let _ = write!(out, "{}", f); + } + Value::Boolean(b) => { + let _ = write!(out, "{}", b); + } + Value::String(s) => out.push_str(s), + Value::HeapRef(r) => { + let _ = write!(out, "[HeapRef {}]", r.0); + } + Value::Null => out.push_str("null"), + } + } + + pub fn string_len_hint(&self) -> usize { + match self { + Value::String(s) => s.len(), + Value::Null => 4, + Value::Boolean(true) => 4, + Value::Boolean(false) => 5, + Value::Int32(i) => i.to_string().len(), + Value::Int64(i) => i.to_string().len(), + Value::Float(f) => f.to_string().len(), + Value::HeapRef(r) => 11 + r.0.to_string().len(), + } + } + #[allow(clippy::inherent_to_string)] pub fn to_string(&self) -> String { - match self { - Value::Int32(i) => i.to_string(), - Value::Int64(i) => i.to_string(), - Value::Float(f) => f.to_string(), - Value::Boolean(b) => b.to_string(), - Value::String(s) => s.clone(), - Value::HeapRef(r) => format!("[HeapRef {}]", r.0), - Value::Null => "null".to_string(), - } + let mut out = String::with_capacity(self.string_len_hint()); + self.append_to_string(&mut out); + out } } diff --git a/crates/console/prometeu-hal/src/host_return.rs b/crates/console/prometeu-hal/src/host_return.rs index 684be4bf..59cb8cae 100644 --- a/crates/console/prometeu-hal/src/host_return.rs +++ b/crates/console/prometeu-hal/src/host_return.rs @@ -22,6 +22,6 @@ impl<'a> HostReturn<'a> { self.stack.push(Value::HeapRef(HeapRef(g as u32))); } pub fn push_string(&mut self, s: String) { - self.stack.push(Value::String(s)); + self.stack.push(Value::String(s.into())); } } diff --git a/crates/console/prometeu-system/src/virtual_machine_runtime/dispatch.rs b/crates/console/prometeu-system/src/virtual_machine_runtime/dispatch.rs index fdf0c963..02ded60d 100644 --- a/crates/console/prometeu-system/src/virtual_machine_runtime/dispatch.rs +++ b/crates/console/prometeu-system/src/virtual_machine_runtime/dispatch.rs @@ -577,7 +577,7 @@ impl NativeInterface for VirtualMachineRuntime { fn expect_string(args: &[Value], index: usize, field: &str) -> Result { match args.get(index).ok_or_else(|| VmFault::Trap(TRAP_TYPE, format!("Missing {}", field)))? { - Value::String(value) => Ok(value.clone()), + Value::String(value) => Ok(value.to_string()), _ => Err(VmFault::Trap(TRAP_TYPE, format!("Expected string {}", field))), } } diff --git a/crates/console/prometeu-vm/src/virtual_machine.rs b/crates/console/prometeu-vm/src/virtual_machine.rs index cef12bfa..215dc584 100644 --- a/crates/console/prometeu-vm/src/virtual_machine.rs +++ b/crates/console/prometeu-vm/src/virtual_machine.rs @@ -639,7 +639,10 @@ impl VirtualMachine { } OpCode::Add => self.binary_op(opcode, start_pc as u32, |a, b| match (&a, &b) { (Value::String(_), _) | (_, Value::String(_)) => { - Ok(Value::String(format!("{}{}", a.to_string(), b.to_string()))) + let mut out = String::with_capacity(a.string_len_hint() + b.string_len_hint()); + a.append_to_string(&mut out); + b.append_to_string(&mut out); + Ok(Value::String(out.into())) } (Value::Int32(a), Value::Int32(b)) => Ok(Value::Int32(a.wrapping_add(*b))), (Value::Int64(a), Value::Int64(b)) => Ok(Value::Int64(a.wrapping_add(*b))), @@ -1213,6 +1216,7 @@ impl VirtualMachine { #[cfg(test)] mod tests { use super::*; + use std::sync::Arc; fn new_test_vm(rom: Vec, constant_pool: Vec) -> VirtualMachine { let rom_len = rom.len() as u32; @@ -1642,6 +1646,19 @@ mod tests { assert_eq!(vm.peek().unwrap(), &Value::String("hello".into())); } + #[test] + fn test_shared_string_clone_reuses_materialized_payload() { + let mut vm = VirtualMachine::default(); + let payload: Arc = "shared-global".into(); + vm.globals.push(Value::String(payload.clone())); + + let cloned = vm.globals[0].clone(); + match cloned { + Value::String(cloned_payload) => assert!(Arc::ptr_eq(&payload, &cloned_payload)), + other => panic!("expected string clone from globals, got {:?}", other), + } + } + #[test] fn test_push_const_invalid_index_traps_oob() { let mut rom = Vec::new(); diff --git a/discussion/index.ndjson b/discussion/index.ndjson index 731a7de7..10905bee 100644 --- a/discussion/index.ndjson +++ b/discussion/index.ndjson @@ -21,7 +21,7 @@ {"type":"discussion","id":"DSC-0026","status":"done","ticket":"render-all-scene-cache-and-camera-integration","title":"Integrate render_all with Scene Cache and Camera","created_at":"2026-04-14","updated_at":"2026-04-18","tags":["gfx","runtime","render","camera","scene"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0031","file":"lessons/DSC-0026-render-all-scene-cache-and-camera-integration/LSN-0031-frame-composition-belongs-above-the-render-backend.md","status":"done","created_at":"2026-04-18","updated_at":"2026-04-18"}]} {"type":"discussion","id":"DSC-0027","status":"done","ticket":"frame-composer-public-syscall-surface","title":"Agenda - FrameComposer Public Syscall Surface","created_at":"2026-04-17","updated_at":"2026-04-18","tags":["gfx","runtime","syscall","abi","frame-composer","scene","camera","sprites"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0032","file":"lessons/DSC-0027-frame-composer-public-syscall-surface/LSN-0032-public-abi-must-follow-the-canonical-service-boundary.md","status":"done","created_at":"2026-04-18","updated_at":"2026-04-18"}]} {"type":"discussion","id":"DSC-0028","status":"done","ticket":"deferred-overlay-and-primitive-composition","title":"Deferred Overlay and Primitive Composition over FrameComposer","created_at":"2026-04-18","updated_at":"2026-04-18","tags":["gfx","runtime","render","frame-composer","overlay","primitives","hud"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0033","file":"lessons/DSC-0028-deferred-overlay-and-primitive-composition/LSN-0033-debug-primitives-should-be-a-final-overlay-not-part-of-game-composition.md","status":"done","created_at":"2026-04-18","updated_at":"2026-04-18"}]} -{"type":"discussion","id":"DSC-0014","status":"review","ticket":"perf-vm-allocation-and-copy-pressure","title":"Agenda - [PERF] VM Allocation and Copy Pressure","created_at":"2026-03-27","updated_at":"2026-04-20","tags":[],"agendas":[{"id":"AGD-0013","file":"workflow/agendas/AGD-0013-perf-vm-allocation-and-copy-pressure.md","status":"accepted","created_at":"2026-03-27","updated_at":"2026-04-20","_override_reason":"User explicitly requested emitting a decision from the resolved agenda in this turn."}],"decisions":[{"id":"DEC-0018","file":"workflow/decisions/DEC-0018-vm-allocation-and-copy-pressure-baseline.md","status":"in_progress","created_at":"2026-04-20","updated_at":"2026-04-20","ref_agenda":"AGD-0013","_override_reason":"User explicitly requested emitting and then accepting the decision, followed by plan generation."}],"plans":[{"id":"PLN-0033","file":"PLN-0033-vm-hot-path-ownership-and-string-copy-pressure.md","status":"review","created_at":"2026-04-20","updated_at":"2026-04-20","ref_decisions":["DEC-0018"]},{"id":"PLN-0034","file":"PLN-0034-internal-allocation-evidence-and-hot-path-measurement.md","status":"review","created_at":"2026-04-20","updated_at":"2026-04-20","ref_decisions":["DEC-0018"]},{"id":"PLN-0035","file":"PLN-0035-runtime-spec-wording-for-materialization-vs-copy-pressure.md","status":"review","created_at":"2026-04-20","updated_at":"2026-04-20","ref_decisions":["DEC-0018"]}],"lessons":[]} +{"type":"discussion","id":"DSC-0014","status":"in_progress","ticket":"perf-vm-allocation-and-copy-pressure","title":"Agenda - [PERF] VM Allocation and Copy Pressure","created_at":"2026-03-27","updated_at":"2026-04-20","tags":[],"agendas":[{"id":"AGD-0013","file":"workflow/agendas/AGD-0013-perf-vm-allocation-and-copy-pressure.md","status":"accepted","created_at":"2026-03-27","updated_at":"2026-04-20","_override_reason":"User explicitly requested emitting a decision from the resolved agenda in this turn."}],"decisions":[{"id":"DEC-0018","file":"workflow/decisions/DEC-0018-vm-allocation-and-copy-pressure-baseline.md","status":"in_progress","created_at":"2026-04-20","updated_at":"2026-04-20","ref_agenda":"AGD-0013","_override_reason":"User explicitly requested emitting and then accepting the decision, followed by plan generation."}],"plans":[{"id":"PLN-0033","file":"PLN-0033-vm-hot-path-ownership-and-string-copy-pressure.md","status":"done","created_at":"2026-04-20","updated_at":"2026-04-20","ref_decisions":["DEC-0018"]},{"id":"PLN-0034","file":"PLN-0034-internal-allocation-evidence-and-hot-path-measurement.md","status":"review","created_at":"2026-04-20","updated_at":"2026-04-20","ref_decisions":["DEC-0018"]},{"id":"PLN-0035","file":"PLN-0035-runtime-spec-wording-for-materialization-vs-copy-pressure.md","status":"review","created_at":"2026-04-20","updated_at":"2026-04-20","ref_decisions":["DEC-0018"]}],"lessons":[]} {"type":"discussion","id":"DSC-0015","status":"open","ticket":"perf-cartridge-boot-and-program-ownership","title":"Agenda - [PERF] Cartridge Boot and Program Ownership","created_at":"2026-03-27","updated_at":"2026-03-27","tags":[],"agendas":[{"id":"AGD-0014","file":"workflow/agendas/AGD-0014-perf-cartridge-boot-and-program-ownership.md","status":"open","created_at":"2026-03-27","updated_at":"2026-03-27"}],"decisions":[],"plans":[],"lessons":[]} {"type":"discussion","id":"DSC-0016","status":"done","ticket":"tilemap-empty-cell-vs-tile-id-zero","title":"Tilemap Empty Cell vs Tile ID Zero","created_at":"2026-03-27","updated_at":"2026-04-09","tags":[],"agendas":[{"id":"AGD-0015","file":"workflow/agendas/AGD-0015-tilemap-empty-cell-vs-tile-id-zero.md","status":"done","created_at":"2026-03-27","updated_at":"2026-04-09"}],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0022","file":"lessons/DSC-0016-tilemap-empty-cell-semantics/LSN-0022-tilemap-empty-cell-convergence.md","status":"done","created_at":"2026-04-09","updated_at":"2026-04-09"}]} {"type":"discussion","id":"DSC-0017","status":"done","ticket":"asset-entry-metadata-normalization-contract","title":"Asset Entry Metadata Normalization Contract","created_at":"2026-03-27","updated_at":"2026-04-09","tags":[],"agendas":[{"id":"AGD-0016","file":"workflow/agendas/AGD-0016-asset-entry-metadata-normalization-contract.md","status":"done","created_at":"2026-03-27","updated_at":"2026-04-09"}],"decisions":[{"id":"DEC-0004","file":"workflow/decisions/DEC-0004-asset-entry-metadata-normalization-contract.md","status":"accepted","created_at":"2026-04-09","updated_at":"2026-04-09"}],"plans":[],"lessons":[{"id":"LSN-0023","file":"lessons/DSC-0017-asset-metadata-normalization/LSN-0023-typed-asset-metadata-helpers.md","status":"done","created_at":"2026-04-09","updated_at":"2026-04-09"}]} diff --git a/discussion/workflow/plans/PLN-0033-vm-hot-path-ownership-and-string-copy-pressure.md b/discussion/workflow/plans/PLN-0033-vm-hot-path-ownership-and-string-copy-pressure.md new file mode 100644 index 00000000..8e2c430c --- /dev/null +++ b/discussion/workflow/plans/PLN-0033-vm-hot-path-ownership-and-string-copy-pressure.md @@ -0,0 +1,108 @@ +--- +id: PLN-0033 +ticket: perf-vm-allocation-and-copy-pressure +title: Plan - VM Hot Path Ownership and String Copy Pressure +status: done +created: 2026-04-20 +completed: 2026-04-20 +tags: [perf, runtime, vm, memory, strings] +--- + +## Briefing + +Implement the runtime-core portion of `DEC-0018` by reducing repeated string payload copies and hot-path allocation pressure without changing guest-visible semantics. + +## Decisions de Origem + +- `DEC-0018` - VM Allocation and Copy Pressure Baseline + +## Alvo + +Change the VM core so that hot paths over numeric values and already-materialized values converge toward zero allocation, while preserving the public semantics of string values and `GET_GLOBAL`. + +## Escopo + +### Included + +- VM value representation and ownership audit for hot paths. +- `GET_GLOBAL` copy-pressure reduction without semantic drift. +- String-producing opcode hot-path changes, especially `ADD`. +- Unit and focused integration coverage for unchanged semantics. + +### Excluded + +- Public ABI changes. +- Certification or host-facing telemetry policy changes. +- Broad speculative string interning or copy-on-write rollout unless directly required by a concrete implementation step. + +## Fora de Escopo + +- Cartridge language changes. +- Host debugger protocol changes. +- General fault/log path rewrites beyond keeping them isolated from hot-path work. + +## Plano de Execucao + +### Step 1 - Audit current hot-path ownership + +**What:** Identify the exact VM operations that still clone or allocate on already-materialized hot paths. +**How:** Review `Value`, `GET_GLOBAL`, `ADD`, related stack helpers, and any nearby string coercion paths; classify each site as unavoidable first materialization versus avoidable repeated copy. +**File(s):** `crates/console/prometeu-bytecode/src/value.rs`, `crates/console/prometeu-vm/src/virtual_machine.rs` + +### Step 2 - Rework internal representation/ownership for expensive payloads + +**What:** Introduce the smallest internal representation change needed to avoid repeated payload copies for expensive values. +**How:** Prefer internal ownership changes that preserve guest-visible semantics. If strings or other expensive payloads need to move behind handles or equivalent internal storage, keep the public value meaning unchanged and constrain the change to runtime-core internals. +**File(s):** `crates/console/prometeu-bytecode/src/value.rs`, `crates/console/prometeu-vm/src/heap.rs`, `crates/console/prometeu-vm/src/object.rs`, `crates/console/prometeu-vm/src/virtual_machine.rs` + +### Step 3 - Fix `GET_GLOBAL` without semantic change + +**What:** Remove avoidable repeated copy pressure from `GET_GLOBAL`. +**How:** Keep the observable contract identical while changing retrieval/storage internals so already-materialized expensive payloads are not recopied on each hot-path access. +**File(s):** `crates/console/prometeu-vm/src/virtual_machine.rs` + +### Step 4 - Optimize string-producing opcode paths + +**What:** Reduce repeated allocation/copy pressure in string-producing operations, starting with `ADD`. +**How:** Separate true new-string creation from repeated cloning of existing payloads. Ensure any operation that semantically creates a new string still does so correctly, while avoiding extra transient allocations around existing materialized values. +**File(s):** `crates/console/prometeu-vm/src/virtual_machine.rs`, related helper modules if introduced + +### Step 5 - Preserve semantic coverage + +**What:** Prove semantics did not change while ownership internals did. +**How:** Add/update tests for constants, dynamic strings, globals, arithmetic/string `ADD`, and edge cases where strings cross stack/global boundaries. +**File(s):** `crates/console/prometeu-vm/src/virtual_machine.rs`, VM test modules + +## Criterios de Aceite + +- `GET_GLOBAL` keeps its public semantics unchanged. +- Hot-path access to already-materialized expensive values no longer performs avoidable repeated payload copies. +- Numeric hot paths remain allocation-free. +- String operations that truly create a new string remain correct and deterministic. +- VM tests cover constant-pool strings, runtime-created strings, and globals under the new ownership model. + +## Tests / Validacao + +### Unit Tests + +- Value/ownership tests for expensive payload movement or referencing. +- VM opcode tests for `GET_GLOBAL`, `SET_GLOBAL`, and string `ADD`. + +### Integration Tests + +- Small bytecode programs that read globals repeatedly and mix numeric/string paths. + +### Manual Verification + +- Review allocation-sensitive hotspots in the final implementation to confirm repeated copies were removed rather than merely relocated. + +## Riscos + +- Internal representation changes may ripple into GC/root traversal. +- A partial fix may hide copies in helper methods rather than eliminate them. +- Over-optimizing strings could accidentally change guest-visible behavior if semantic boundaries are not defended by tests. + +## Dependencies + +- `DEC-0018` +- Existing VM heap/object/root traversal contracts diff --git a/discussion/workflow/plans/PLN-0034-internal-allocation-evidence-and-hot-path-measurement.md b/discussion/workflow/plans/PLN-0034-internal-allocation-evidence-and-hot-path-measurement.md new file mode 100644 index 00000000..57101428 --- /dev/null +++ b/discussion/workflow/plans/PLN-0034-internal-allocation-evidence-and-hot-path-measurement.md @@ -0,0 +1,98 @@ +--- +id: PLN-0034 +ticket: perf-vm-allocation-and-copy-pressure +title: Plan - Internal Allocation Evidence and Hot Path Measurement +status: review +created: 2026-04-20 +completed: +tags: [perf, runtime, telemetry, allocation, engineering] +--- + +## Briefing + +Add internal engineering evidence for `DEC-0018` so the team can verify progress toward zero allocation on the happy path without turning that metric into a certification contract. + +## Decisions de Origem + +- `DEC-0018` - VM Allocation and Copy Pressure Baseline + +## Alvo + +Produce low-intrusion internal measurement for allocation and copy pressure that can guide implementation and regression detection during engineering work. + +## Escopo + +### Included + +- Internal-only allocation counters or equivalent evidence mechanisms. +- Measurement points around VM execution slices and/or frame boundaries. +- Regression-oriented tests or harnesses that assert expected internal evidence in happy-path scenarios. + +### Excluded + +- New public certification outputs. +- Guest-visible profiling ABI changes. +- Heavy per-opcode instrumentation that distorts the hot path. + +## Fora de Escopo + +- Rewriting the full telemetry model. +- Publishing allocation counts to cartridge authors as normative behavior. + +## Plano de Execucao + +### Step 1 - Define internal evidence model + +**What:** Choose the minimal evidence surface that proves whether happy-path execution allocates. +**How:** Prefer counters or snapshots that can distinguish first materialization from steady-state hot-path execution. Tie the evidence to engineering diagnostics only. +**File(s):** `crates/console/prometeu-hal/src/telemetry.rs`, `crates/console/prometeu-system/src/virtual_machine_runtime/tick.rs`, internal runtime/telemetry helpers as needed + +### Step 2 - Implement low-intrusion collection + +**What:** Add instrumentation with bounded overhead. +**How:** Record allocation evidence at frame or execution-slice boundaries rather than on every operation when possible. Keep the instrumentation isolated from guest-visible contracts. +**File(s):** `crates/console/prometeu-vm/src/heap.rs`, `crates/console/prometeu-system/src/virtual_machine_runtime/tick.rs`, related telemetry modules + +### Step 3 - Define regression scenarios + +**What:** Create deterministic scenarios that express the engineering target. +**How:** Add tests or bench-like checks for numeric happy paths, already-materialized constant strings, and runtime-created string paths after first materialization. +**File(s):** runtime/VM test modules; dedicated internal telemetry tests if appropriate + +### Step 4 - Document how evidence is used + +**What:** Clarify that this evidence is an engineering metric, not certification policy. +**How:** Update inline docs or internal notes near the measurement code so future work does not accidentally publish the metric as a hard contract. +**File(s):** relevant telemetry/runtime modules + +## Criterios de Aceite + +- Engineers can observe whether selected happy-path scenarios allocate. +- The measurement surface distinguishes internal engineering evidence from public certification output. +- Instrumentation overhead stays bounded and does not require intrusive per-opcode tracing. +- Regression checks exist for at least one numeric path and one already-materialized string path. + +## Tests / Validacao + +### Unit Tests + +- Counter/reset/snapshot correctness for internal allocation evidence. + +### Integration Tests + +- Runtime scenarios that verify zero-allocation expectations on steady-state happy paths. + +### Manual Verification + +- Inspect final measurement plumbing to confirm it is not exposed as public certification policy. + +## Riscos + +- Instrumentation may accidentally become hot-path overhead. +- Counters may undercount or overcount if first materialization is not separated from steady-state execution. +- Internal metrics may be misread later as external compatibility guarantees unless explicitly documented. + +## Dependencies + +- `DEC-0018` +- Existing runtime telemetry boundary diff --git a/discussion/workflow/plans/PLN-0035-runtime-spec-wording-for-materialization-vs-copy-pressure.md b/discussion/workflow/plans/PLN-0035-runtime-spec-wording-for-materialization-vs-copy-pressure.md new file mode 100644 index 00000000..807d2f58 --- /dev/null +++ b/discussion/workflow/plans/PLN-0035-runtime-spec-wording-for-materialization-vs-copy-pressure.md @@ -0,0 +1,88 @@ +--- +id: PLN-0035 +ticket: perf-vm-allocation-and-copy-pressure +title: Plan - Runtime Spec Wording for Materialization vs Copy Pressure +status: review +created: 2026-04-20 +completed: +tags: [spec, runtime, memory, strings, perf] +--- + +## Briefing + +Apply the minimal spec wording cleanup implied by `DEC-0018` so published runtime docs distinguish first materialization cost from repeated hot-path copy pressure without changing the public ABI. + +## Decisions de Origem + +- `DEC-0018` - VM Allocation and Copy Pressure Baseline + +## Alvo + +Update canonical runtime specs so the published memory/value/debug model reflects the accepted architectural framing behind the implementation work. + +## Escopo + +### Included + +- Wording updates in runtime specs for values, memory/allocation, and debug/profiling boundaries. +- Clarifications that zero-allocation on the happy path is an engineering target, not certification policy. + +### Excluded + +- Any new guest-visible ABI rules. +- Broad documentation rewrite unrelated to `DEC-0018`. + +## Fora de Escopo + +- Lessons material. +- Cartridge author migration guides. + +## Plano de Execucao + +### Step 1 - Identify exact normative gaps + +**What:** Locate wording that currently conflates all allocation cost with hot-path copy pressure or leaves the first-materialization distinction implicit. +**How:** Review the accepted decision against the current value, memory, and debug chapters and capture only the gaps required to keep docs aligned with implementation intent. +**File(s):** `docs/specs/runtime/02a-vm-values-and-calling-convention.md`, `docs/specs/runtime/03-memory-stack-heap-and-allocation.md`, `docs/specs/runtime/10-debug-inspection-and-profiling.md` + +### Step 2 - Update normative text + +**What:** Clarify constant-pool materialization, runtime materialization, and repeated-copy pressure boundaries. +**How:** Add concise normative wording that preserves the existing ABI while making clear that internal engineering may target zero allocation on happy paths without publishing that as certification policy. +**File(s):** `docs/specs/runtime/02a-vm-values-and-calling-convention.md`, `docs/specs/runtime/03-memory-stack-heap-and-allocation.md`, `docs/specs/runtime/10-debug-inspection-and-profiling.md` + +### Step 3 - Cross-check consistency + +**What:** Ensure the updated docs stay aligned with the accepted decision and with any implementation evidence introduced under linked plans. +**How:** Review terminology across the edited chapters and confirm no text implies a guest-visible ABI or certification promise that the decision explicitly rejected. +**File(s):** same as above + +## Criterios de Aceite + +- Runtime specs distinguish first materialization from repeated copy pressure. +- Specs do not imply a public ABI change for `GET_GLOBAL` or strings. +- Specs do not promote zero-allocation happy-path behavior to certification policy. +- Edited docs remain consistent across values, memory, and profiling chapters. + +## Tests / Validacao + +### Unit Tests + +- Not applicable. + +### Integration Tests + +- Not applicable. + +### Manual Verification + +- Read the edited chapters side by side with `DEC-0018` and confirm that every new normative statement maps directly to the accepted decision. + +## Riscos + +- Specs may drift ahead of implementation details if wording is too specific. +- Overstating performance goals in normative docs may accidentally create an external compatibility promise. + +## Dependencies + +- `DEC-0018`