Skip to content

perf(semantics): read a library symbol's implied generals from its record - #1357

Merged
HuiJun merged 3 commits into
developfrom
fix/implied-base-usage-perf
Oct 21, 2026
Merged

HuiJun merged 3 commits into
developfrom
fix/implied-base-usage-perf

Conversation

@devin-ai-integration

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

Copy link
Copy Markdown
Contributor

What and why

Develop's CircleCI race and coverage jobs exceed their 30-minute package budget in internal/exec/runtime and cmd/sysml since d8b60ed (usages subset the base usage of their kind): go test ./cmd/sysml -run TestExploreTablesTheSpacecraftRaceWithinItsBudget went from 155 s to 211 s on one machine. #1350 memoized Model.ImplicitGenerals per symbol, which brought that test back to 158 s on develop; what remained is that the memo is per model, and the runtime builds a model per execution, so a library symbol's implied generals were derived again — candidateBaseFeatures, declaredGeneralizationReaches over the library — by every model that asked (statedTypes/effectiveTypes through DeclaredSupertypes). On the internal/exec/runtime suite that derivation was 11.7 s of 415 s of CPU (ImplicitGenerals 12.2 s cumulative), with unitConversion, isBound and longName derived thousands of times each.

A library symbol's facts already record its direct supertypes; they now record its implied generals too, and ImplicitGenerals answers from the record whenever DirectSupertypes does, memoizing the answer like the derived one:

  • LibraryFacts.ImpliedSupers lists every general the kind implies — the base definition (Parts::Part) as well as the base usage (Parts::parts) — in derivation order; it listed the base usages only, so answering from it lost the implied typing (the reason ImplicitGenerals read facts for Recorded() symbols alone).
  • The per-document cache record (factRecord.Implied, impliedOf) carries the same entries, since the loader installs those facts before the interface records are written; a record whose implied general no reference reaches leaves the whole edge set to be derived on load, as a supertype does.
  • internal/workspace/libs/stdlib.snapshot regenerated (go generate ./internal/workspace/libs with a fresh XDG_CACHE_HOME; a stale cache record would have been read back as the fact).

No behaviour change: the same generals in the same order, read instead of derived.

Baseline movement (re-recorded over develop with usages subsetting their base usage)

  • docs/project/pilot-rejection-baseline.json (go run -C tools ./cmd/pilot-reject -update): 334 cases, 323 both reject, 11 only we reject, 0 only the pilot rejects, 0 both accept — every bucket keeps its verdict. One row moves: semantic/k49-binding-connector-with-three-ends.kerml ours-errors 2 → 3, the new one connector-ends ("b has 3 ends but specializes a binary link (Links::BinaryLink)"): the connector now conforms to Links::binaryLinks, so the end-count rule of KerML 1.1 §8.3.4.5.3 fires beside binding-binary; the pilot rejects it as before.
  • docs/project/api-json-differential-baseline.json (go run -C tools ./cmd/pilot-apijson-diff -update, all nine roots provisioned): headline unchanged — 920 files, 86974 matched, 29497 ours-only, 40387 pilot-only, 596664 present-equal, 47633 present-different, 1021444 ours-missing, 65920 pilot-missing, 2020649 absent-both, 20867 indeterminate — so docs/project/api-json-differential.md does not move; within spec-examples one element changes bucket to a type mismatch (typeMismatch 390 → 391, total 1742 → 1743) and extensionProperties 140676 → 140679. make docs-counts reports every figure current.

How it was verified

  • Failing-before test TestLibraryImpliedGeneralsAreRecordedWithTheDefinition (internal/workspace/model): a library usage's record carries an implied definition, ImplicitGenerals matches the record, a repeated query allocates 0 times and a fresh model answers the same — fails on d8b60ed (no definition recorded), passes here. TestEveryPersistedFactFamilyIsCompared now compares the new family.

  • Timings on this machine (go test -count=1, same VM, runs sequential; "before" b60a18c, "after" d8b60ed, "develop" 5b0c985 = develop with fix(semantics): derive a declaration's implied generals once per symbol #1350, "fix" this branch):

    package / test before after develop fix
    cmd/sysml -run TestExploreTablesTheSpacecraftRaceWithinItsBudget 154.9 s 211.1 s 158.4 s 157.5 s
    internal/exec/runtime (whole package, no race) 193 s wall 238 s wall 190 s wall 209 s wall (shared the machine with another test for ~1 min; profile below)

    CPU profile of TestExecutionConformance: ImplicitGenerals 0.63 s / computeImplicitGenerals 0.54 s of 52 s sampled (develop: 12.2 s / 11.7 s of 415 s on the whole package); what remains under statedTypes is declaredTypes resolution, which perf(runtime): compute a feature's effective types once per model #1351 memoizes.

  • gofmt -l . empty, go build ./..., go vet, go test of internal/semantic/..., internal/workspace/..., internal/check/..., internal/exec/runtime, cmd/sysml; python3 scripts/changelog.py check. Full gate on the branch: go build ./..., go vet ./..., gofmt -l . empty, go test -timeout 60m ./... (every corpus provisioned) exit 0, go test -C tools ./... exit 0, make lint passed.

Checklist

  • make test and make lint pass locally
  • Tests added or updated for the change
  • Documentation extended where it already covers the surface (see CONTRIBUTING.md)
  • Changelog entry added as changes/unreleased/<slug>.<section>.md, not as an edit to CHANGELOG.md
  • baselines regenerated and make docs-counts run if a gate count moved (compliance rows need nothing: the census is counted at docs build)
  • No internal work-item labels (waves, slices, F4, K5) in the body, docs, or changelog

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

…cord

A library symbol's implied generals were derived again by every model that queried them, since the memo is per model and the runtime builds one per execution; its record now carries them (base definition and base usage) and ImplicitGenerals answers from it, as DirectSupertypes does.

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

Recorded over the tree where usages subset the base usage of their kind: one rejection case gains a connector-ends error, every bucket keeps its verdict; the api-json figures are unchanged.

Co-Authored-By: jason.han <hanhuijun@gmail.com>
@devin-ai-integration
devin-ai-integration Bot marked this pull request as ready for review October 21, 2026 15:58
devin-ai-integration[bot]

This comment was marked as resolved.

Co-Authored-By: jason.han <hanhuijun@gmail.com>
@HuiJun
HuiJun merged commit 0dddbfd into develop Oct 21, 2026
27 checks passed
@HuiJun
HuiJun deleted the fix/implied-base-usage-perf branch October 21, 2026 19:38
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