Skip to content

perf(runtime): note the chain being evaluated without copying its slices into every context - #1745

Merged
HuiJun merged 3 commits into
developfrom
perf/eval-context-size
Oct 30, 2026
Merged

HuiJun merged 3 commits into
developfrom
perf/eval-context-size

Conversation

@devin-ai-integration

Copy link
Copy Markdown
Contributor

What and why

AssignmentLoopStep allocates 971 KB per step on the 0.12.0 candidate against 921 KB on 0.11.0's source built with the same toolchain (six rounds, bytes exact; allocation counts are equal). Half of that is the size of EvalContext, which the runtime copies for every nested evaluation: #1514 added chainStatic with two slice headers (the chain's segments and the declarations they name, 48 bytes), and #1461 added the openInputs flag in its own padded word, so a context grew from 152 to 184 bytes.

This PR keeps what both merges compute and shrinks how the context notes it:

  • chainStatic holds a pointer to the chain's last segment, its length and a pointer to the memoized chainDecls instead of two slice headers; chainDeclarationsFor compares the last segment's address as before, so the whole-chain / suffix distinction is unchanged. The Model.chainDeclarations memo stores *chainDecls, so the memoized slice is shared, not copied, into each context.
  • openInputs sits beside the behavior-body flag so the two share a word.

EvalContext is 152 bytes again. Root cause: the per-context copy of chain state (#1514) and padding (#1461); correctness of both merges is kept.

A/B, six rounds (go test ./internal/exec/runtime -bench . -benchmem -count 1 × 6, benchstat; pre-#1514 / #1514 / candidate 1c6bb1972 / this branch; same host, "Intel(R) Xeon(R) Processor", 8 CPUs; bytes and allocations exact)

Row pre-#1514 #1514 candidate fix 0.11.0 source, go1.27.3
AssignmentLoopStep B/op 912.7 Ki 974.1 Ki (+6.73 %) 947.9 Ki (+3.86 %) 925.7 Ki (+1.42 %) 899.7 Ki
AssignmentLoopStep allocs/op 8 536 9 335 8 539 8 539 8 531
DerivedReadWriteRead B/op 2.656 Ki 2.750 Ki (+3.5 %) 3.250 Ki (+22.4 %) 3.188 Ki (+20.0 %) 2.6 Ki
DerivedReadWriteRead allocs/op 67 67 71 71 67
every other row of the package, B/op and allocs/op within ±0.2 % of the candidate

Residual, stated in the fragment: AssignmentLoopStep stays 13 Ki above pre-#1514 (the context's new state — the chain note and the open-inputs flag — is what the merges needed); the remaining DerivedReadWriteRead bytes are #1619's order marks on derived reads, a separate PR (perf/derive-marks-footprint). No sec/op claim: the host ran a full go test ./... gate alongside rounds 1–3 and the timing columns carry ±100–250 % spread.

Memory acceptance

runtime.test -test.bench '^BenchmarkAssignmentLoopStep$' -test.benchtime 300x, candidate and fix built the same way, three cold runs each, interleaved (/usr/bin/time, 8 CPUs):

run candidate peak RSS wall fix peak RSS wall
1 117.5 MiB 0.82 s 112.7 MiB 0.81 s
2 115.4 MiB 0.82 s 119.6 MiB 0.82 s
3 119.8 MiB 0.83 s 115.9 MiB 0.82 s

Hard cap: under ulimit -v 4000000 both pass (candidate 122 MiB, fix 118 MiB peak RSS). Under ulimit -v 2000000 both revisions die as the Go runtime reserves its arenas (runtime: out of memory: cannot allocate 4194304-byte block (24 MB in use)), before any model is loaded, so that cap says nothing about the change. Whole binary, sysml -validate over the 12 086-element generated model, three cold runs: 198 / 198 / 198 MiB candidate, 200 / 187 / 200 MiB fix, 0.24 s both; tests/stressmodel FleetInstantiate/512 and Load/512 stay inside their GC-pacing spread (1.14–1.47 GiB and 1.27–1.36 GiB on both). Resident memory is not what this change recovers: it is 23 KB of allocation volume per step, and the PR does not claim an RSS change.

How it was verified

  • TestChainDeclarationsAreResolvedOncePerModel (new, internal/exec/runtime/chain_declarations_memo_test.go) pins that a chain's declarations are resolved once per model and that the contexts evaluating it share the memoized entry.
  • The execution conformance and trace suites, robustness suites and the gRPC conformance cases pass unchanged (go test ./... below).

Gate

$ go build ./...        # ok
$ go vet ./...          # ok
$ gofmt -l .            # (empty)
$ go test -timeout 45m ./...
ok   124 packages, 0 FAIL, exit 0

(corpora present: training examples, pilot corpora, spec examples, pilot library XMI, PSSM suite, with their OPENSYSML_REQUIRE_* variables set.)

Checklist

  • go build ./..., go vet ./..., gofmt -l . (empty), go test ./... pass locally (above); make lint not run (the gate above is the repository's definition of done)
  • Tests added or updated for the change
  • Documentation extended where it already covers the surface — none covers EvalContext's layout
  • Changelog entry added as changes/unreleased/eval-context-size.fixed.md
  • baselines regenerated and make docs-counts run if a gate count moved — no count moved
  • No internal work-item labels in the body, docs, or changelog

Link to Devin session: https://nasa-jpl-demo.devinenterprise.com/sessions/15408827f8104c7fbbdbd1b2c852ed2c
Open in Devin Desktop: https://nasa-jpl-demo.devinenterprise.com/desktop/session/15408827f8104c7fbbdbd1b2c852ed2c?variant=devin
Requested by: @HuiJun

devin-ai-integration Bot and others added 3 commits October 30, 2026 03:16
…ces into every context

Co-Authored-By: jason.han <hanhuijun@gmail.com>
…k requires

Co-Authored-By: jason.han <hanhuijun@gmail.com>
Co-Authored-By: jason.han <hanhuijun@gmail.com>
@devin-ai-integration

Copy link
Copy Markdown
Contributor Author

I'll fix CI failures and address comments from users with write access. I'll skip comments containing "(aside)".

  • Disable automatic comment, CI, and merge conflict monitoring

@devin-ai-integration
devin-ai-integration Bot marked this pull request as ready for review October 30, 2026 09:45

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Devin Review: No Issues Found

Devin Review analyzed this PR and found no bugs or issues to report.

Devin Review

@HuiJun
HuiJun merged commit 68c80ee into develop Oct 30, 2026
30 of 51 checks passed
@HuiJun
HuiJun deleted the perf/eval-context-size branch October 30, 2026 12:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant