Skip to content

perf(check): sweep a document's expressions for typed by only while one is unreported - #1744

Merged
HuiJun merged 4 commits into
developfrom
perf/typed-by-sweep
Oct 30, 2026
Merged

HuiJun merged 4 commits into
developfrom
perf/typed-by-sweep

Conversation

@devin-ai-integration

@devin-ai-integration devin-ai-integration Bot commented Oct 30, 2026 •

Copy link
Copy Markdown
Contributor

What and why

Cause: #1478 (typed by on body-expression parameters). Its NonstandardNotationPass sweep, typedByInExpressions, walks every expression of every document on every load to find a typed by spelled on a body-expression parameter, allocating a walker frame per expression whether or not the document spells one — and SysML documents almost never do. That is the +4.7 % B/op on every load-heavy row of the 0.12.0 candidate (LoadModel/250…4000, AnalyseResolved), bisected to #1478.

This PR has the parser count the typed by spellings of a document (RootNamespace.TypedBy, incremented in Parser.typedBySpan, the one place the keyword is accepted), and the pass runs the expression sweep only while the member walk has reported fewer than that. The count is syntax, so it lives on the AST root like UndefinedOperators and is carried through astcodec; snapshotFormatVersion moves to 27 so a snapshot written without the field is refused rather than decoded shifted, and stdlib.snapshot is regenerated. A speculative parse (tryParseCrossFeature reading an end's typing before the end does) holds and restores the count with its checkpoint, so an end without a cross feature is counted once. The finding itself is unchanged: a document that spells typed by in a body expression is still swept and still reported (internal/check/passes tests pass unchanged; TestRootNamespaceCountsTypedBy pins the count on declarations, prefix metadata and body-expression parameters).

How it was verified

A/B, six rounds, same host (Intel(R) Xeon(R) Processor, 8 CPUs), benchstat

Columns: #1478's parent / #1478 / the 0.12.0 candidate (1c6bb1972) / this branch. Bytes and allocations are exact; the host is shared with the benchmark queue, so no sec/op claim is made.

internal/frontend/repl

Row pre-#1478 #1478 candidate fix 0.11.0 source on go1.27.3
LoadModel/250 B/op 13.94 Mi 14.59 Mi (+4.64 %) 14.71 Mi (+5.51 %) 14.09 Mi (+1.05 %) 13.96 Mi
LoadModel/1000 B/op 55.43 Mi 58.05 Mi (+4.72 %) 58.33 Mi (+5.23 %) 55.84 Mi (+0.74 %) 55.52 Mi
LoadModel/4000 B/op 221.7 Mi 232.2 Mi (+4.74 %) 233.5 Mi (+5.33 %) 223.4 Mi (+0.76 %) 222.3 Mi
LoadModel/1000 B/element 56.76 Ki 59.44 Ki 59.73 Ki 57.18 Ki —
LoadModel/0 B/op 246.8 Ki 246.7 Ki 246.8 Ki 246.9 Ki 227.9 Ki (the +8 % is #1462, its own PR)
LoadModel/* allocs/op 520.5k 520.7k 538.3k 538.2k (unchanged: #1478 added bytes, not allocations) —
LoadModel/* live-B/op 7.373 Mi 7.384 Mi 7.369 Mi 7.406 Mi (~, retained heap is not what moved) —

internal/workspace/model

Row pre-#1478 #1478 candidate fix
AnalyseResolved B/op 648.0 Ki 666.9 Ki (+2.91 %) 667.9 Ki (+3.08 %) 649.8 Ki (+0.29 %)
AnalyseUnresolved B/op 13.85 Mi 13.87 Mi 13.90 Mi 13.89 Mi
both, allocs/op unchanged

Residual, stated in the fragment: LoadModel/1000 allocates 58.5 MB per load instead of 61.2 MB, against 58.2 MB on 0.11.0's source with the same toolchain; the 0.3 MB left (+0.5 %) is other merges' work, not #1478's. The other load-heavy rows the release record lists under #1478 (tests/stressmodel Load/LoadFiles/OpenSplit/ValidateSplit/EditImported, ImportValues, WorkspaceEdit, REPLSubmitSnippet) run the same pass per document and are expected to move alike; they were not re-measured for this PR and are not claimed here.

Memory acceptance (repl.test -bench 'LoadModel/elements=1000$' -benchtime 20x, three cold runs each, candidate / fix)

wall peak RSS
run 1 2.49 / 2.43 s 162 120 / 161 968 KiB
run 2 2.50 / 2.42 s 162 468 / 163 408 KiB
run 3 2.48 / 2.41 s 161 156 / 158 364 KiB
under ulimit -v 4000000 2.48 / 2.42 s, rc 0 162 568 / 162 820 KiB, rc 0

Peak RSS does not move: the sweep's garbage was short-lived, so this fix recovers allocation volume (GC work), not resident memory — the record says the same.

Gate

On cd077d9 (the head, with the checkpoint restore and snapshot format 27); the A/B above is from 755faab, whose only difference is those two review fixes, neither on a benchmarked path.

$ go build ./...        # ok
$ go vet ./...          # ok
$ gofmt -l .            # (empty)
$ go test -timeout 45m ./...
ok   124 packages, 0 FAIL, exit 0 (corpora present: training, pilot, spec examples, PSSM, pilot library XMI;
     full log kept with the release record's raw outputs)
ok   github.com/Open-MBEE/OpenSysML/tests/wasm 294.367s   # last line

python3 scripts/changelog.py check passes with changes/unreleased/typed-by-sweep.fixed.md.

Checklist

  • go build ./..., go vet ./..., gofmt -l . (empty) and go test ./... pass locally (above); make lint is gofmt + go vet
  • Tests added or updated for the change (TestRootNamespaceCountsTypedBy)
  • Documentation: the release performance record (docs(perf): measure the 0.12.0 candidate against 0.11.0 #1740) carries this row; the pass's user-facing behaviour is unchanged, so its docs are too
  • Changelog entry added as changes/unreleased/typed-by-sweep.fixed.md
  • No gate count moved; stdlib.snapshot regenerated for the new RootNamespace.TypedBy field
  • 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 02:56
…ne is unreported

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:30
devin-ai-integration[bot]

This comment was marked as resolved.

devin-ai-integration Bot and others added 2 commits October 30, 2026 08:34
tryParseCrossFeature reads an end's typing before the end's own declaration does, so a count the checkpoint did not hold was one too high, and the notation pass swept the document's expressions for a typing it had already reported.

Co-Authored-By: jason.han <hanhuijun@gmail.com>
A snapshot written before RootNamespace.TypedBy was encoded passed the version and digest checks and would have decoded the field from the next one's bytes; format 27 refuses it.

Co-Authored-By: jason.han <hanhuijun@gmail.com>
@HuiJun
HuiJun merged commit 5dd0724 into develop Oct 30, 2026
30 checks passed
@HuiJun
HuiJun deleted the perf/typed-by-sweep 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