diff --git a/ast/term.go b/ast/term.go index 5e95c13821..67198ed3c7 100644 --- a/ast/term.go +++ b/ast/term.go @@ -1260,9 +1260,10 @@ func newset(n int) *set { keys = make([]*Term, 0, n) } return &set{ - elems: make(map[int]*Term, n), - keys: keys, - hash: 0, + elems: make(map[int]*Term, n), + keys: keys, + hash: 0, + ground: true, } } @@ -1275,9 +1276,10 @@ func SetTerm(t ...*Term) *Term { } type set struct { - elems map[int]*Term - keys []*Term - hash int + elems map[int]*Term + keys []*Term + hash int + ground bool } // Copy returns a deep copy of s. @@ -1287,14 +1289,13 @@ func (s *set) Copy() Set { cpy.Add(x.Copy()) }) cpy.hash = s.hash + cpy.ground = s.ground return cpy } // IsGround returns true if all terms in s are ground. func (s *set) IsGround() bool { - return !s.Until(func(x *Term) bool { - return !x.IsGround() - }) + return s.ground } // Hash returns a hash code for s. @@ -1550,6 +1551,7 @@ func (s *set) insert(x *Term) { s.elems[hash] = x s.keys = append(s.keys, x) s.hash = 0 + s.ground = s.ground && x.IsGround() } func (s *set) get(x *Term) *Term { diff --git a/topdown/topdown_bench_test.go b/topdown/topdown_bench_test.go index be62aaea18..2469140288 100644 --- a/topdown/topdown_bench_test.go +++ b/topdown/topdown_bench_test.go @@ -19,6 +19,76 @@ import ( "github.com/open-policy-agent/opa/util" ) +func BenchmarkArrayIteration(b *testing.B) { + sizes := []int{10, 100, 1000, 10000} + for _, n := range sizes { + b.Run(fmt.Sprint(n), func(b *testing.B) { + benchmarkIteration(b, getArrayIterationBenchmarkModule(n)) + }) + } +} + +func BenchmarkSetIteration(b *testing.B) { + sizes := []int{10, 100, 1000, 10000} + for _, n := range sizes { + b.Run(fmt.Sprint(n), func(b *testing.B) { + benchmarkIteration(b, getSetIterationBenchmarkModule(n)) + }) + } +} + +func BenchmarkObjectIteration(b *testing.B) { + sizes := []int{10, 100, 1000, 10000} + for _, n := range sizes { + b.Run(fmt.Sprint(n), func(b *testing.B) { + benchmarkIteration(b, getObjectIterationBenchmarkModule(n)) + }) + } +} + +func benchmarkIteration(b *testing.B, module string) { + ctx := context.Background() + query := ast.MustParseBody("data.test.main") + compiler := ast.MustCompileModules(map[string]string{ + "test.rego": module, + }) + + b.ResetTimer() + + for i := 0; i < b.N; i++ { + + q := NewQuery(query).WithCompiler(compiler) + _, err := q.Run(ctx) + if err != nil { + b.Fatal(err) + } + } +} + +func getArrayIterationBenchmarkModule(n int) string { + return fmt.Sprintf(`package test + + fixture = [ x | x := numbers.range(1, %d)[_] ] + + main { fixture[i] }`, n) +} + +func getSetIterationBenchmarkModule(n int) string { + return fmt.Sprintf(`package test + + fixture = { x | x := numbers.range(1, %d)[_] } + + main { fixture[i] }`, n) +} + +func getObjectIterationBenchmarkModule(n int) string { + return fmt.Sprintf(`package test + + fixture = { x: x | x := numbers.range(1, %d)[_] } + + main { fixture[i] }`, n) +} + func BenchmarkLargeJSON(b *testing.B) { data := generateLargeJSONBenchmarkData() ctx := context.Background()