Skip to content

perf(runtime): memoize related features only for features that declare the relationship - #1743

Merged
HuiJun merged 2 commits into
developfrom
perf/related-features-memo
Oct 30, 2026
Merged

HuiJun merged 2 commits into
developfrom
perf/related-features-memo

Conversation

@devin-ai-integration

Copy link
Copy Markdown
Contributor

What and why

Part of the 0.12.0 performance gate (record: #1740). #1626 memoized, per (feature, owner type, relationship kind), the features a feature's relationships name, so that a repeated read allocates nothing. The memo's key, map growth and the clipped empty slice were paid for every feature the walk asked about — and most features declare no subsetting, redefinition or reference of the kind asked for. Those entries were the majority of the map and grew with every type an instantiation touched: FeaturesOf went from 267 MiB to 296 MiB per run and a cold Instantiate from 221 MB to 252 MB (column 2 → column 3 of the record, same toolchain).

relatedFeatures now returns nil before touching the memo when relationshipsOfKind(sym, kind) is empty (that lookup is itself memoized by #1626 and is what the collect loop iterates). The result is the same value the memo would have stored (slices.Clip of a nil slice), so the walks that do relate stay memoized and repeated reads still allocate nothing.

Residual, stated in the fragment: FeaturesOf allocates 285 MB per run instead of 310 MB (0.11.0 on the same toolchain: 273 MB), BatchSatisfy 476 MiB instead of 491 MiB (0.11.0: 461 MiB), a cold Instantiate 230 MB instead of 252 MB (0.11.0: 221 MB). The rest is the memo that pays for itself (allocation counts on these rows are 4–8 % below 0.11.0 because of it) — no parity claim. Allocation counts are unchanged by this PR; GRPCVerifyConstraint is unchanged (it is the subject of #1742).

How it was verified

Six interleaved rounds of go test ./tests/perf -run '^$' -bench . -benchmem on the merge before #1626, #1626 itself, the 0.12.0 candidate (1c6bb1972) and this branch; benchstat, 8 CPUs, Intel(R) Xeon(R) Processor, GOMAXPROCS=8. Bytes/allocs are exact; this host is loaded, so no sec/op claim is made except where the fix's own spread is tight.

row B/op pre-#1626 #1626 candidate this PR 0.11.0 src, go1.27.3
FeaturesOf 267.1Mi ± 0% 296.0Mi (+10.8 %) 295.7Mi 271.2Mi (+1.5 % vs pre-#1626, p=0.002) 261.2Mi
BatchSatisfy 461.0Mi 491.2Mi (+6.6 %) 491.2Mi 475.5Mi (+3.2 %, p=0.002) 460Mi
Instantiate (cold, pinned -benchtime 2x, 3 runs each) 220.6 MB 251.8 MB 251.8 MB 229.8 MB 221 MB
Instantiate (suite, b.N-dependent) 210.5Mi ± 46% 240.2Mi ± 40% 240.2Mi ± 40% 219.2Mi ± 0% —
ExecuteActionFreshContext 48.48Ki 49.37Ki 48.24Ki 47.83Ki (−0.9 %) —
GRPCVerifyConstraint 1537.9Ki 813.6Ki 814.6Ki 814.6Ki (unchanged) —

allocs/op: FeaturesOf 2.186M → 2.185M, BatchSatisfy 4.078M → 4.078M, Instantiate 2.011M → 2.010M, GRPCVerifyConstraint 16.30k → 16.30k (all unchanged). FeaturesOf sec/op 641.8m ± 310 % → 598.6m ± 2 % (−6.7 %, p=0.002); every other tests/perf row ~.

tests/stressmodel (Load|Satisfy|FleetInstantiate|FleetSatisfy, six rounds, candidate vs this PR): every row ~ on sec/op, B/op and allocs/op (FleetInstantiate/512 B/op 176.1Mi ± 8 % vs 181.3Mi ± 5 %, p=0.13; allocs identical).

Memory acceptance (/usr/bin/time -v, the tests/perf test binary running FeaturesOf + Instantiate, candidate vs this PR, 3 cold runs each): peak RSS 336 340 / 336 784 / 334 840 KiB → 317 904 / 317 624 / 319 820 KiB (−17 MiB), wall 1.85 / 1.84 / 1.84 s → 1.75 / 1.79 / 1.75 s. Under a hard cap ulimit -v 4000000: candidate 335 140 KiB, this PR 316 956 KiB, both rc=0 (a Go test binary's address-space reservation is what sets the lowest cap it tolerates, so the cap is on virtual size, not RSS). tests/stressmodel FleetInstantiate/512 binary, 3 cold runs: 192 720 / 187 180 / 187 744 KiB → 196 488 / 198 632 / 197 888 KiB (+2–5 %, within that row's B/op spread above; its allocation count is identical) — recorded, not claimed either way.

Gate (this branch, corpora present — OPENSYSML_REQUIRE_PSSM_SUITE/PILOT_LIBRARY_XMI assets fetched with their scripts; -timeout 45m because the full suite ran beside the benchmark queue):

$ go build ./...        # ok
$ go vet ./...          # ok
$ gofmt -l .            # (empty)
$ go test -timeout 45m ./...
ok   124 packages, 0 FAIL  (full log kept with the record's raw outputs)
   116 packages passed in the first run; the other 8 (internal/exec/runtime, internal/exec/smt,
   tests/codegen, tests/export, tests/kpar, tests/migrate, tests/model, tests/wasm) failed only on
   "no space left on device" when /tmp filled beside the benchmark queue, and all 8 pass re-run
   with the disk freed:
ok   github.com/Open-MBEE/OpenSysML/internal/exec/runtime 363.223s
ok   github.com/Open-MBEE/OpenSysML/internal/exec/smt     382.539s
ok   github.com/Open-MBEE/OpenSysML/tests/codegen         484.722s
ok   github.com/Open-MBEE/OpenSysML/tests/export          315.395s
ok   github.com/Open-MBEE/OpenSysML/tests/kpar             91.492s
ok   github.com/Open-MBEE/OpenSysML/tests/migrate         111.166s
ok   github.com/Open-MBEE/OpenSysML/tests/model           319.392s
ok   github.com/Open-MBEE/OpenSysML/tests/wasm            314.798s

Checklist

  • make test and make lint pass locally (go build ./..., go vet ./..., gofmt -l . empty, go test ./... above)
  • Tests added or updated for the change — none: pure allocation change, behaviour pinned by the existing internal/exec/runtime subsetting/redefinition tests and TestExecutionConformance
  • Documentation extended where it already covers the surface — the 0.12.0 performance record (docs(perf): measure the 0.12.0 candidate against 0.11.0 #1740) carries the measured outcome
  • Changelog entry added as changes/unreleased/related-features-memo.fixed.md
  • baselines regenerated and make docs-counts run if a gate count moved — no gate 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 2 commits October 30, 2026 03:48
…e the relationship

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

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 08:09

@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 2a73695 into develop Oct 30, 2026
30 of 51 checks passed
@HuiJun
HuiJun deleted the perf/related-features-memo branch October 30, 2026 12:43
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