From 0742a39ce155834ccd0d4473b1ffaf1450804269 Mon Sep 17 00:00:00 2001 From: Anders Eknert Date: Mon, 11 Nov 2024 19:03:43 +0100 Subject: [PATCH] Optimize biunify for arrays The construction of intermediate throwaway `ast.Array`s caused almost 5 million allocations when running `regal lint` against its own bundle. While the results are likely more impressive for Regal policies than the average one, this is still a substantial improvement across a large group of policies. **regal lint OPA main** ``` BenchmarkRegalLintingItself-10 1 3317838417 ns/op 6611412800 B/op 124873123 allocs/op ``` **regal lint this branch** ``` BenchmarkRegalLintingItself-10 1 3140384416 ns/op 6590159176 B/op 120098613 allocs/op ``` Signed-off-by: Anders Eknert --- topdown/eval.go | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/topdown/eval.go b/topdown/eval.go index 6f24ca9bc4..930ae0a3d8 100644 --- a/topdown/eval.go +++ b/topdown/eval.go @@ -932,6 +932,22 @@ func (e *eval) biunifyArraysRec(a, b *ast.Array, b1, b2 *bindings, iter unifyIte }) } +func (e *eval) biunifyTerms(a, b []*ast.Term, b1, b2 *bindings, iter unifyIterator) error { + if len(a) != len(b) { + return nil + } + return e.biunifyTermsRec(a, b, b1, b2, iter, 0) +} + +func (e *eval) biunifyTermsRec(a, b []*ast.Term, b1, b2 *bindings, iter unifyIterator, idx int) error { + if idx == len(a) { + return iter() + } + return e.biunify(a[idx], b[idx], b1, b2, func() error { + return e.biunifyTermsRec(a, b, b1, b2, iter, idx+1) + }) +} + func (e *eval) biunifyObjects(a, b ast.Object, b1, b2 *bindings, iter unifyIterator) error { if a.Len() != b.Len() { return nil @@ -1962,7 +1978,7 @@ func (e evalFunc) evalOneRule(iter unifyIterator, rule *ast.Rule, cacheKey ast.R child.traceEnter(rule) - err := child.biunifyArrays(ast.NewArray(e.terms[1:]...), ast.NewArray(args...), e.e.bindings, child.bindings, func() error { + err := child.biunifyTerms(e.terms[1:], args, e.e.bindings, child.bindings, func() error { return child.eval(func(child *eval) error { child.traceExit(rule)