Reuse prepared execution snapshot ownership

This commit is contained in:
2026-10-09 01:08:46 +00:00
parent b7b4000aab
commit 80aecb646d
4 changed files with 200 additions and 7 deletions

View File

@@ -105,8 +105,11 @@ one execution-precedence and error-ordering implementation while allowing a
full backend pool to reject work before expensive schema, artifact, and
rendering operations. `Prepare` discards the plan after returning its public
metadata. `Run` retains the plan through initial and repaired-output validation
and discards it when the operation ends. Prepared execution stores the same
kind of plan only in its private payload.
and discards it when the operation ends. Prepared execution transfers the
completed preparation's owned message, input-hash, and copied-schema values
into its private payload, while deep-copying mutable provider extras and
creating an independent details snapshot. Claim completion or discard clears
the execution payload without removing the details snapshot.
Pointer-based numeric overrides preserve an explicit zero. Invalid negative or
out-of-range values fail as invalid requests. Endpoint overrides do not change

View File

@@ -3,7 +3,7 @@
Implement the remaining local work in the [performance roadmap](performance.md)
in order. Each sequential stage is scoped for one Luna coding-agent prompt.
Update status and evidence as each work unit lands.
Stages 1–12 are complete; Stages 13–14 remain pending. The completed
Stages 1–13 are complete; Stage 14 remains pending. The completed
inspection plan is preserved at
`481e2d4:docs/roadmap/implementation.md` in Git history.
@@ -496,9 +496,17 @@ message length as deep-copied bytes, and document lifetime assumptions.
## Stage 13 — Conditionally Consolidate Prepared Creation's First Snapshot Copy
**Outcome:** Either one justified creation-copy simplification with preserved
ownership, or a completed, evidence-backed decision to retain the current
implementation. This does not authorize skipping measurement or ownership work.
**Status: Complete (2026-10-09).** The selected transfer was retained. Prepared
execution now reuses preparation-owned messages, hashes, and schema, while
deep-copying nested provider extras and keeping an independent details
snapshot. Source-mutation and invalid-copy coverage pass; benchmark evidence
shows meaningful reductions for mutable payloads without a material increase
in retained heap.
**Outcome:** The ownership inventory supports transferring preparation-owned
values. The simplified creation path is retained with its nested-extra copy,
independent details snapshot, and existing cleanup and caller-ownership
boundaries.
**Selected candidate:** Transfer preparation's already-owned messages, hashes,
and schema into its private execution payload instead of deep-copying them
@@ -892,6 +900,68 @@ separate details clone. Focused verification passed: `go test -mod=readonly
./internal/usecase .`; representative content, copied schema, fake validation
status, and post-completion details were checked outside timing.
### Prepared Creation Snapshot Comparison
The before measurements use the Stage 11 assembled benchmark fixture committed
at `743e78d` and the Stage 12 ownership fixture committed at `11ecb92`. The
after measurements use the Stage 13 candidate in this completion. Both runs
used the same fixtures and environment: Linux/amd64, Intel Core i5-7260U @
2.20 GHz, benchmark parallelism 4, Go 1.26.8, ConfigKit `v0.5.0`, JSON Schema
`v6.0.2`, and backend modules `v3.20260930.0`. Benchmarks ran without the race
detector. The commands are those in the Assembled Workflow Benchmark Baseline
and Prepared Snapshot Ownership And Live-Heap Baseline subsections above.
The assembled creation benchmark measures `PrepareExecution` followed by
discarding its unused handle. Times list five `ns/op` samples, then median and
range; allocations are `B/op; allocs/op`.
| Case | Before `ns/op` (median; range) | Before allocations | After `ns/op` (median; range) | After allocations |
| --- | --- | --- | --- | --- |
| Small | 8194, 8016, 8095, 8073, 7939 (8073; 7939–8194) | 5210; 56 | 11028, 11059, 11756, 12279, 11089 (11089; 11028–12279) | 4665; 53 |
| Large message | 937857, 955672, 919810, 1058503, 1094114 (955672; 919810–1094114) | 873679–873681; 56 | 1751124, 1647596, 1761392, 1556071, 1584325 (1647596; 1556071–1761392) | 873135–873144; 53 |
| Mutable payload | 8114785, 8394320, 10387083, 8418604, 8528766 (8418604; 8114785–10387083) | 3711282–3711329; 60727 | 12157300, 12363416, 12185745, 11868982, 21225784 (12185745; 11868982–21225784) | 3483089–3483252; 56324–56326 |
Small and large cases each save about 0.5 KB and three allocations, while the
mutable case saves about 228 KB and 4,401–4,403 allocations. The after timing
samples in this run are higher than the recorded before samples, while other
repetitions on the same machine varied substantially; the timing evidence
does not establish a reliable latency improvement or regression. The ordinary
`Prepare`, `Run`, and public `Details()` allocation counts are unchanged within
their recorded baseline ranges; this candidate only changes prepared-execution
creation.
The focused internal snapshot benchmark isolates the same creation path using
the Stage 12 fake-collaborator fixture. Times again list five samples, median,
and range.
| Snapshot case | Before `ns/op` (median; range) | Before allocations | After `ns/op` (median; range) | After allocations |
| --- | --- | --- | --- | --- |
| Small | 45776, 51524, 46933, 46834, 44663 (46834; 44663–51524) | 13014–13016; 216 | 50907, 79812, 62186, 65569, 62599 (62599; 50907–79812) | 9405–9406; 156 |
| Mutable payload | 5343223, 5234718, 4213550, 4419520, 5046181 (5046181; 4213550–5343223) | 1211931–1212104; 25243–25244 | 5991662, 5508812, 6244981, 5176459, 4365122 (5508812; 4365122–6244981) | 983033–983309; 20837–20839 |
The internal fixture saves about 3.6 KB/60 allocations for the small case and
about 229 KB/4,404–4,407 allocations for the mutable case. Timing samples vary
between repeated runs and do not establish a consistent speedup or regression.
The retained-heap comparison uses the documented fixed batch of four mutable-
payload handles. These are five independently repeated `retained-B` samples
after the candidate; before samples are recorded in the baseline subsection.
| Handle state | Before samples (median; range) | After samples (median; range) |
| --- | --- | --- |
| Ready | 1458352, 1458256, 1459320, 1459048, 1459160 (1459048; 1458256–1459320) | 1458936, 1459048, 1460592, 1460464, 1460192 (1460192; 1458936–1460592) |
| Completed | 731040, 730768, 729352, 729448, 729624 (729624; 729352–731040) | 731312, 730496, 730768, 730544, 731040 (730768; 730496–731312) |
| Discarded | 728560, 730928, 730544, 730496, 730496 (730496; 728560–730928) | 731040, 731088, 730720, 730768, 730608 (730768; 730608–731088) |
All before/after retained ranges overlap. The median shifts are about 1.1 KB
for ready and completed handles and 272 B for discarded handles, all small
relative to their live sets and within observed run-to-run variation. This is
not evidence of a material retention increase, nor an exact object-size
guarantee. Together with the measured allocation reduction and mutation tests,
it supports retaining the simple ownership transfer while keeping the nested
extra-map copy, details clone, lifecycle cleanup, and caller-owned conversions.
Focused validation passed: `go test -race -mod=readonly ./internal/usecase .`.
### Validation And Compiled-Rendering Baseline
The benchmark-only baseline is commit