internal/core/adt: cache evaluation results for dynamic fields
Also cache string index lookup. This is fast, but requires
an RWLock, which can add up.
Issue #572
Change-Id: I782cf52f50aa022b069b98de9d0496775c40e30e
Reviewed-on: https://cue-review.googlesource.com/c/cue/+/8562
Reviewed-by: CUE cueckoo <cueckoo@gmail.com>
Reviewed-by: Paul Jolly <paul@myitcv.org.uk>
Reviewed-by: Marcel van Lohuizen <mpvl@golang.org>
diff --git a/internal/core/adt/closed.go b/internal/core/adt/closed.go
index acf50ac..3e1e434 100644
--- a/internal/core/adt/closed.go
+++ b/internal/core/adt/closed.go
@@ -310,6 +310,10 @@
ctx.generation++
ctx.todo = nil
+ // TODO(perf): more aggressively determine whether a struct is open or
+ // closed: open structs do not have to be checked, yet they can particularly
+ // be the ones with performance isssues, for instanced as a result of
+ // embedded for comprehensions.
for _, s := range n.Structs {
if !s.useForAccept() {
continue
@@ -317,11 +321,16 @@
markCounts(ctx, s.CloseInfo)
}
+ str := ""
+ if f.IsString() {
+ str = f.StringValue(ctx)
+ }
+
for _, s := range n.Structs {
if !s.useForAccept() {
continue
}
- if verifyArc(ctx, s, f) {
+ if verifyArc(ctx, s, f, str) {
// Beware: don't add to below expression: this relies on the
// side effects of markUp.
ok := markUp(ctx, s.closeInfo, 0)
@@ -435,7 +444,7 @@
return x
}
-func verifyArc(ctx *OpContext, s *StructInfo, f Feature) bool {
+func verifyArc(ctx *OpContext, s *StructInfo, f Feature, label string) bool {
isRegular := f.IsRegular()
o := s.StructLit
@@ -455,10 +464,16 @@
return false
}
- for _, b := range o.Dynamic {
- m := dynamicMatcher{b.Key}
- if m.Match(ctx, env, f) {
- return true
+ if f.IsString() {
+ for _, b := range o.Dynamic {
+ v := env.evalCached(ctx, b.Key)
+ s, ok := v.(*String)
+ if !ok {
+ continue
+ }
+ if label == s.Str {
+ return true
+ }
}
}
diff --git a/internal/core/adt/composite.go b/internal/core/adt/composite.go
index b476662..7cbaef4 100644
--- a/internal/core/adt/composite.go
+++ b/internal/core/adt/composite.go
@@ -228,6 +228,8 @@
Embedding bool
}
+// TODO(perf): this could be much more aggressive for eliminating structs that
+// are immaterial for closing.
func (s *StructInfo) useForAccept() bool {
if c := s.closeInfo; c != nil {
return !c.noCheck
diff --git a/internal/core/adt/optional.go b/internal/core/adt/optional.go
index 05568bd..8a452c9 100644
--- a/internal/core/adt/optional.go
+++ b/internal/core/adt/optional.go
@@ -125,26 +125,6 @@
return false
}
-type dynamicMatcher struct {
- expr Expr
-}
-
-func (m dynamicMatcher) Match(c *OpContext, env *Environment, f Feature) bool {
- if !f.IsRegular() || !f.IsString() {
- return false
- }
- v, ok := c.Evaluate(env, m.expr)
- if !ok {
- return false
- }
- s, ok := v.(*String)
- if !ok {
- return false
- }
- label := f.StringValue(c)
- return label == s.Str
-}
-
type patternMatcher Conjunct
func (m patternMatcher) Match(c *OpContext, env *Environment, f Feature) bool {