Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changes/unreleased/resolve-first-lazy-roots.changed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
- The REPL's dependency search collects the root namespaces a submission imports or references only when the submission left a reference unresolved, instead of walking every submission's text twice. Loading a 1 000-element model allocates 14 130 fewer times per load (532 578 vs 546 708 allocations; 533 100 before the search was added) and takes 124 ms instead of 144 ms (109 ms before, same machine).
35 changes: 35 additions & 0 deletions internal/frontend/repl/resolve_walk_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package repl

import (
"fmt"
"strings"
"testing"

"github.com/Open-MBEE/OpenSysML/internal/syntax/parser"
"github.com/Open-MBEE/OpenSysML/internal/syntax/source"
)

func resolvedParses(n int) []parsed {
var sb strings.Builder
for i := 0; i < n; i++ {
fmt.Fprintf(&sb, "package P%d { attribute a%d = %d; }\n", i, i, i)
}
root := parser.New(source.New("typed", []byte(sb.String()))).ParseFile()
return []parsed{{root: root}}
}

// A submission that leaves nothing unresolved looks for no root namespace, so
// its text is not walked: the work does not grow with the text.
func TestResolvingNothingDoesNotWalkTheText(t *testing.T) {
s, dir := workspaceSession(t)
search := &rootSearch{find: s.workspaceIndex().Declaring, where: "under " + dir, onDemand: true}
cost := func(n int) float64 {
parses := resolvedParses(n)
return testing.AllocsPerRun(20, func() {
s.resolveReferences(Result{}, parses, search)
})
}
if small, large := cost(50), cost(100); large > small {
t.Errorf("resolving 100 resolved packages allocates %v times, 50 packages %v: the text is walked", large, small)
}
}
17 changes: 12 additions & 5 deletions internal/frontend/repl/workspace.go
Original file line number Diff line number Diff line change
Expand Up @@ -124,11 +124,6 @@ func (s *Session) resolveReferences(res Result, parses []parsed, search *rootSea
if search == nil {
return workspaceLoad{}
}
var imported, referenced []string
for _, pre := range parses {
imported = append(imported, project.ImportedRoots(pre.root)...)
referenced = append(referenced, project.ReferencedRoots(pre.root)...)
}
var subjects []string
for _, d := range res.Diagnostics {
if d.Code != resolve.CodeUnresolved || !res.mine(d.Span) {
Expand All @@ -138,10 +133,22 @@ func (s *Session) resolveReferences(res Result, parses []parsed, search *rootSea
subjects = append(subjects, subject)
}
}
if len(subjects) == 0 {
// Nothing unresolved: no root is looked for, so the text is not walked.
return workspaceLoad{}
}
unresolved := func(name string) bool { return referenceBegins(subjects, name) }
var imported []string
for _, pre := range parses {
imported = append(imported, project.ImportedRoots(pre.root)...)
}
ws := s.loadDeclaring(s.undeclaredRoots(imported, unresolved), search)
if !ws.loaded {
// An import no file answers must not keep the qualified names waiting.
var referenced []string
for _, pre := range parses {
referenced = append(referenced, project.ReferencedRoots(pre.root)...)
}
more := s.loadDeclaring(s.undeclaredRoots(referenced, unresolved), search)
more.notices = append(ws.notices, more.notices...)
ws = more
Expand Down
Loading