Record prepared snapshot ownership evidence
This commit is contained in:
@@ -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–11 are complete; Stages 12–14 remain pending. The completed
|
||||
Stages 1–12 are complete; Stages 13–14 remain pending. The completed
|
||||
inspection plan is preserved at
|
||||
`481e2d4:docs/roadmap/implementation.md` in Git history.
|
||||
|
||||
@@ -457,6 +457,13 @@ separate from engine construction and provider latency.
|
||||
|
||||
## Stage 12 — Establish Snapshot Ownership And Live-Heap Evidence
|
||||
|
||||
**Status: Complete (2026-10-09).** Internal snapshot and fixed-batch retained-
|
||||
heap benchmarks are committed at `11ecb92`. The ownership inventory confirms
|
||||
that message/cache metadata, hashes, and schema are preparation-owned, while
|
||||
nested `ExtraParams` still need a deep copy before any transfer. Repeated
|
||||
measurements and lifecycle assumptions are recorded below; no production
|
||||
behavior changed.
|
||||
|
||||
**Outcome:** A measured ownership inventory for Stage 13's conditional change.
|
||||
No production change; keep this analysis separate from creating the assembled
|
||||
workflow benchmarks.
|
||||
@@ -785,6 +792,106 @@ than point claims. Focused verification passed: `go test -mod=readonly .`;
|
||||
representative rendered messages, prepared details, generated output, and
|
||||
structured validation were checked outside benchmark timing.
|
||||
|
||||
### Prepared Snapshot Ownership And Live-Heap Baseline
|
||||
|
||||
The benchmark-only baseline is committed at `11ecb92` (`Add prepared snapshot
|
||||
ownership benchmarks`). `BenchmarkPreparedSnapshot` exercises the internal
|
||||
runner with the existing fake prompt/profile repositories, renderer, artifact
|
||||
reader, a stateless injected client, and a fast prepared validator. It measures
|
||||
`PrepareExecution` and discard without compiling a schema. Its small fixture
|
||||
uses one rendered message, no provider extras, and a one-property schema. The
|
||||
mutable fixture uses eight rendered messages plus one appended message, cache
|
||||
metadata on each, one input/hash, 64 nested provider-option entries, and a
|
||||
256-property schema. Fixture checks run outside timing; each measured handle is
|
||||
discarded and only the latest discarded handle is retained as a sink.
|
||||
|
||||
`BenchmarkRetainedPreparedHandles` uses the mutable fixture and a fixed batch
|
||||
of four handles. After fixture setup and checks, it preallocates an empty handle
|
||||
slice, forces GC, and records a setup-only `HeapAlloc` baseline. It then retains
|
||||
four ready, completed, or discarded handles, forces GC again, and reports the
|
||||
heap delta. The retained benchmark command uses `-benchtime=1x`; the single
|
||||
timed operation is followed by filling the remaining batch slots outside the
|
||||
timer. Both samples call `runtime.KeepAlive` for the runner, request, and
|
||||
retained batch. The metric is a live-heap difference, not a derivation from
|
||||
`B/op`.
|
||||
|
||||
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`.
|
||||
|
||||
Snapshot command (five 500 ms samples, without the race detector):
|
||||
|
||||
```sh
|
||||
GOTOOLCHAIN=local GOWORK=off GOPROXY=off GONOPROXY=none GOSUMDB=off \
|
||||
go test -mod=readonly -run '^$' -bench '^BenchmarkPreparedSnapshot$' \
|
||||
-benchmem -benchtime=500ms -count=5 ./internal/usecase
|
||||
```
|
||||
|
||||
Times list the five `ns/op` samples followed by median and range; allocation
|
||||
figures are `B/op; allocs/op` (ranges where they varied).
|
||||
|
||||
| Snapshot case | `ns/op` samples (median; range) | Allocations |
|
||||
| --- | --- | --- |
|
||||
| Small | 45776, 51524, 46933, 46834, 44663 (46834; 44663–51524) | 13014–13016; 216 |
|
||||
| Mutable payload | 5343223, 5234718, 4213550, 4419520, 5046181 (5046181; 4213550–5343223) | 1211931–1212104; 25243–25244 |
|
||||
|
||||
Retained-heap command (five fixed-batch samples):
|
||||
|
||||
```sh
|
||||
GOTOOLCHAIN=local GOWORK=off GOPROXY=off GONOPROXY=none GOSUMDB=off \
|
||||
go test -mod=readonly -run '^$' -bench '^BenchmarkRetainedPreparedHandles$' \
|
||||
-benchmem -benchtime=1x -count=5 ./internal/usecase
|
||||
```
|
||||
|
||||
| Retained handle state | `retained-B` samples (median; range) | Timed-operation allocations |
|
||||
| --- | --- | --- |
|
||||
| Ready | 1458352, 1458256, 1459320, 1459048, 1459160 (1459048; 1458256–1459320) | 1211896–1213256 B; 25242–25251 |
|
||||
| Completed | 731040, 730768, 729352, 729448, 729624 (729624; 729352–731040) | 1213024–1214368 B; 25255–25264 |
|
||||
| Discarded | 728560, 730928, 730544, 730496, 730496 (730496; 728560–730928) | 1211896–1213256 B; 25242–25251 |
|
||||
|
||||
The ready four-handle batch retains about 1.46 MB above setup; completed and
|
||||
discarded batches retain about 0.73 MB. The roughly 0.73 MB difference is
|
||||
consistent with the extra execution snapshot held only while a handle is
|
||||
ready. After claim/run or discard, `payload.clear` removes that execution
|
||||
payload while the independent details snapshot remains available. Heap deltas
|
||||
include runtime/GC noise; the five observed ranges are reported rather than
|
||||
treated as an exact object-size guarantee.
|
||||
|
||||
The source ownership inventory is:
|
||||
|
||||
- `completePreparationWithStructuredOutput` builds a fresh input-hash map and
|
||||
obtains rendered messages through `domain.ConcatRenderedMessages`, which
|
||||
copies message slices and each `CacheControl` pointer while sharing immutable
|
||||
string contents.
|
||||
- `structuredOutputFromValidationPlan` copies the validation plan's schema
|
||||
document with `jsonvalue.Copy` before storing it in the prepared run.
|
||||
- `resolveExecutionTarget` and `mergeExecutionTargetOverride` use the
|
||||
shallow-copy helper `copyExtraParams`. The configuration repository's full
|
||||
`GetProfile` and the public request conversion deep-copy extra values, but
|
||||
internal profile fakes return only shallow struct copies and internal
|
||||
`RunRequest` override maps may be source-owned. Nested extras can therefore
|
||||
remain aliased through the runner's outer-map copy. Stage 13 must keep a
|
||||
`jsonvalue.CopyMap` boundary before transferring the prepared run.
|
||||
- `PrepareExecution` deep-clones `operation.run` for execution, then clones
|
||||
that snapshot again for details. `Details()` deep-clones details per call;
|
||||
the root facade conversion also creates caller-owned maps, schema, messages,
|
||||
and cache metadata. Claim completion and `Discard` clear only the private
|
||||
payload, so the details copy survives both lifecycles.
|
||||
- The injected-client adapter converts generated requests to caller-owned
|
||||
prompt messages/cache metadata, execution-target extra maps, and structured
|
||||
schema values. Its client cannot mutate the private prepared snapshot through
|
||||
a generation request.
|
||||
|
||||
The assembled public `Details()` benchmark independently measured 670315–670339
|
||||
B and 11548 allocations for one mutable-payload details call. The internal
|
||||
snapshot benchmark isolates creation with fakes; together these baselines show
|
||||
that mutable schema/extras copies dominate the small message-slice cost. The
|
||||
Stage 13 candidate can remove the execution clone of preparation-owned
|
||||
messages, hashes, and schema, but must retain a deep copy of extras and the
|
||||
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.
|
||||
|
||||
### Validation And Compiled-Rendering Baseline
|
||||
|
||||
The benchmark-only baseline is commit
|
||||
|
||||
Reference in New Issue
Block a user