perf(verifier): memoize scopedElements per tree

scopedElements rebuilt a full tree walk plus map on every candidatesForVerb call (~16 per step). Cache the result keyed on lastTree and invalidate it in PushSnapshot.
This commit is contained in:
pj committed 2026-06-11 09:28:35 +05:30
1 parent d3182239dc
commit 4c931635d7
1 file changed
+10 -2
+10 -2
View File
@@ -35,6 +35,8 @@ type Verifier struct {
witnesses map[string]Witness witnesses map[string]Witness
lastTree *hierarchy.Tree lastTree *hierarchy.Tree
scopeCache map[*hierarchy.Element]bool
scopeCacheTree *hierarchy.Tree
lastAction *Action lastAction *Action
lastLogs []LogEntry lastLogs []LogEntry
lastExceptions []Exception lastExceptions []Exception
@@ -256,6 +258,7 @@ func (v *Verifier) buildFormulaNode(index int) (ltl.Formula, error) {
// allowed and yields an empty ax scope. // allowed and yields an empty ax scope.
func (v *Verifier) PushSnapshot(input SnapshotInput) error { func (v *Verifier) PushSnapshot(input SnapshotInput) error {
v.lastTree = input.Tree v.lastTree = input.Tree
v.scopeCache = nil
v.lastAction = input.LastAction v.lastAction = input.LastAction
v.lastLogs = input.Logs v.lastLogs = input.Logs
v.lastExceptions = input.Exceptions v.lastExceptions = input.Exceptions
@@ -604,14 +607,16 @@ const frameworkPackage = "android"
// With no app package configured (iOS/web, or an unscoped run) every node is in // With no app package configured (iOS/web, or an unscoped run) every node is in
// scope, preserving prior behavior. // scope, preserving prior behavior.
func (v *Verifier) scopedElements() map[*hierarchy.Element]bool { func (v *Verifier) scopedElements() map[*hierarchy.Element]bool {
if v.scopeCacheTree == v.lastTree && v.scopeCache != nil {
return v.scopeCache
}
scope := make(map[*hierarchy.Element]bool, len(v.lastTree.Elements)) scope := make(map[*hierarchy.Element]bool, len(v.lastTree.Elements))
unscoped := v.appPackage == "" unscoped := v.appPackage == ""
if v.lastTree.Root == nil { if v.lastTree.Root == nil {
for _, element := range v.lastTree.Elements { for _, element := range v.lastTree.Elements {
scope[element] = true scope[element] = true
} }
return scope } else {
}
var walk func(node *hierarchy.Node, owner string) var walk func(node *hierarchy.Node, owner string)
walk = func(node *hierarchy.Node, owner string) { walk = func(node *hierarchy.Node, owner string) {
if pkg := node.Element.Package; pkg != "" && pkg != frameworkPackage { if pkg := node.Element.Package; pkg != "" && pkg != frameworkPackage {
@@ -625,6 +630,9 @@ func (v *Verifier) scopedElements() map[*hierarchy.Element]bool {
} }
} }
walk(v.lastTree.Root, "") walk(v.lastTree.Root, "")
}
v.scopeCache = scope
v.scopeCacheTree = v.lastTree
return scope return scope
} }