From 5a754bc043b8e8c2d57cf906386734a73761e2a2 Mon Sep 17 00:00:00 2001 From: Bryan Boreham Date: Tue, 21 Sep 2021 13:07:36 +0100 Subject: [PATCH] Short-circuit vector binary ops (#9362) In degenerate cases we can save the effort of building a map. Signed-off-by: Bryan Boreham --- promql/bench_test.go | 3 +++ promql/engine.go | 16 ++++++++++++++++ 2 files changed, 19 insertions(+) diff --git a/promql/bench_test.go b/promql/bench_test.go index a1501a3cb..eb020d2f1 100644 --- a/promql/bench_test.go +++ b/promql/bench_test.go @@ -130,6 +130,9 @@ func BenchmarkRangeQuery(b *testing.B) { { expr: "a_X unless b_X{l=~'.*[0-4]$'}", }, + { + expr: "a_X and b_X{l='notfound'}", + }, // Simple functions. { expr: "abs(a_X)", diff --git a/promql/engine.go b/promql/engine.go index 8379bf48e..3b1d35757 100644 --- a/promql/engine.go +++ b/promql/engine.go @@ -1767,6 +1767,9 @@ func (ev *evaluator) VectorAnd(lhs, rhs Vector, matching *parser.VectorMatching, if matching.Card != parser.CardManyToMany { panic("set operations must only use many-to-many matching") } + if len(lhs) == 0 || len(rhs) == 0 { + return nil // Short-circuit: AND with nothing is nothing. + } // The set of signatures for the right-hand side Vector. rightSigs := map[string]struct{}{} @@ -1788,6 +1791,11 @@ func (ev *evaluator) VectorOr(lhs, rhs Vector, matching *parser.VectorMatching, if matching.Card != parser.CardManyToMany { panic("set operations must only use many-to-many matching") } + if len(lhs) == 0 { // Short-circuit. + return rhs + } else if len(rhs) == 0 { + return lhs + } leftSigs := map[string]struct{}{} // Add everything from the left-hand-side Vector. @@ -1808,6 +1816,11 @@ func (ev *evaluator) VectorUnless(lhs, rhs Vector, matching *parser.VectorMatchi if matching.Card != parser.CardManyToMany { panic("set operations must only use many-to-many matching") } + // Short-circuit: empty rhs means we will return everything in lhs; + // empty lhs means we will return empty - don't need to build a map. + if len(lhs) == 0 || len(rhs) == 0 { + return lhs + } rightSigs := map[string]struct{}{} for _, sh := range rhsh { @@ -1827,6 +1840,9 @@ func (ev *evaluator) VectorBinop(op parser.ItemType, lhs, rhs Vector, matching * if matching.Card == parser.CardManyToMany { panic("many-to-many only allowed for set operators") } + if len(lhs) == 0 || len(rhs) == 0 { + return nil // Short-circuit: nothing is going to match. + } // The control flow below handles one-to-one or many-to-one matching. // For one-to-many, swap sidedness and account for the swap when calculating