[PERF] PromQL: Reduce allocations when walking syntax tree

Currently it allocates at least once on every recursion, and since
`append` gives no extra space for 1 or 2 elements, will allocate
several times for even modestly complex expressions.

Instead, allocate space for 4 elements to begin, and defer adding
another until we need it.

Add a note that `path` may get overwritten; this was true previously.

Signed-off-by: Bryan Boreham <bjboreham@gmail.com>
This commit is contained in:
Bryan Boreham 2025-05-11 12:17:22 +01:00
parent ceac4d2418
commit e4789cf89f

View File

@ -334,10 +334,13 @@ func Walk(v Visitor, node Node, path []Node) error {
if v, err = v.Visit(node, path); v == nil || err != nil {
return err
}
path = append(path, node)
var pathToHere []Node // Initialized only when needed.
for e := range ChildrenIter(node) {
if err := Walk(v, e, path); err != nil {
if pathToHere == nil {
pathToHere = append(path, node)
}
if err := Walk(v, e, pathToHere); err != nil {
return err
}
}
@ -371,8 +374,10 @@ func (f inspector) Visit(node Node, path []Node) (Visitor, error) {
// Inspect traverses an AST in depth-first order: It starts by calling
// f(node, path); node must not be nil. If f returns a nil error, Inspect invokes f
// for all the non-nil children of node, recursively.
// Note: path may be overwritten after f returns; copy path if you need to retain it.
func Inspect(node Node, f inspector) {
Walk(f, node, nil) //nolint:errcheck
var pathBuf [4]Node // To reduce allocations during recursion.
Walk(f, node, pathBuf[:0]) //nolint:errcheck
}
// ChildrenIter returns an iterator over all child nodes of a syntax tree node.