Skip to content

Commit

Permalink
Merge #88638
Browse files Browse the repository at this point in the history
88638: eval: reduce interface->interface conversions in ComparisonWithSubOperator r=mgartner a=mgartner

#### colexecproj: add benchmark for colexecproj.defaultCmpRConstProjOp

Release note: None

#### eval: reduce interface->interface conversions in ComparisonWithSubOperator

Consider the schema and queries below.

    CREATE TABLE a (a STRING);
    INSERT INTO a SELECT 'abc' || i::STRING FROM generate_series(1, 500) g(i);

    CREATE TABLE b (b STRING);
    INSERT INTO b SELECT 'abc' || i::STRING FROM generate_series(1, 75000) g(i);

    CREATE TABLE c (c STRING);

    SELECT * FROM b
    WHERE b IN (SELECT a FROM a) OR
          b IN (SELECT c FROM c);

A significant portion of time is spent performing `tree.Expr` to
`tree.Datum` conversions when evaluating the `IN` expressions. This
commit eliminates those unnecessary conversions, drastically speeding up
execution of the query.

Release note (performance improvement): Some types of queries with
comparisons with constant values now execute faster.


Co-authored-by: Marcus Gartner <marcus@cockroachlabs.com>
  • Loading branch information
craig[bot] and mgartner committed Sep 27, 2022
2 parents 6711a8b + 8965de8 commit 60cac1e
Show file tree
Hide file tree
Showing 4 changed files with 88 additions and 14 deletions.
51 changes: 41 additions & 10 deletions pkg/sql/colexec/colexecproj/default_cmp_op_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ package colexecproj
import (
"context"
"fmt"
"strconv"
"strings"
"testing"

"github.com/cockroachdb/cockroach/pkg/settings/cluster"
Expand Down Expand Up @@ -129,16 +131,45 @@ func BenchmarkDefaultCmpProjOp(b *testing.B) {
Settings: st,
},
}
for _, useSel := range []bool{false, true} {
for _, hasNulls := range []bool{false, true} {
inputTypes := []*types.T{types.String, types.String}
name := fmt.Sprintf("IS DISTINCT FROM/useSel=%t/hasNulls=%t", useSel, hasNulls)
benchmarkProjOp(b, name, func(source *colexecop.RepeatableBatchSource) (colexecop.Operator, error) {
return colexectestutils.CreateTestProjectingOperator(
ctx, flowCtx, source, inputTypes,
"@1 IS DISTINCT FROM @2", testMemAcc,
)
}, inputTypes, useSel, hasNulls)
var sb strings.Builder
sb.WriteString("@1 = ANY (")
for i := 0; i < 500; i++ {
if i > 1 {
sb.WriteByte(',')
}
sb.WriteString("'abc")
sb.WriteString(strconv.Itoa(i))
sb.WriteByte('\'')
}
sb.WriteByte(')')
eqAny := sb.String()
benchCases := []struct {
name string
expr string
typs []*types.T
}{
{
name: "IS DISTINCT FROM",
expr: "@1 IS DISTINCT FROM @2",
typs: []*types.T{types.String, types.String},
},
{
name: "eq ANY const",
expr: eqAny,
typs: []*types.T{types.String},
},
}
for _, benchCase := range benchCases {
for _, useSel := range []bool{false, true} {
for _, hasNulls := range []bool{false, true} {
name := fmt.Sprintf("%s/useSel=%t/hasNulls=%t", benchCase.name, useSel, hasNulls)
benchmarkProjOp(b, name, func(source *colexecop.RepeatableBatchSource) (colexecop.Operator, error) {
return colexectestutils.CreateTestProjectingOperator(
ctx, flowCtx, source, benchCase.typs,
benchCase.expr, testMemAcc,
)
}, benchCase.typs, useSel, hasNulls)
}
}
}
}
4 changes: 2 additions & 2 deletions pkg/sql/sem/eval/comparison.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,8 +86,8 @@ func evalDatumsCmp(
continue
}

_, newLeft, newRight, _, not := tree.FoldComparisonExpr(subOp, left, elem)
d, err := BinaryOp(ctx, fn.EvalOp, newLeft.(tree.Datum), newRight.(tree.Datum))
_, newLeft, newRight, _, not := tree.FoldComparisonExprWithDatums(subOp, left, elem)
d, err := BinaryOp(ctx, fn.EvalOp, newLeft, newRight)
if err != nil {
return nil, err
}
Expand Down
4 changes: 2 additions & 2 deletions pkg/sql/sem/eval/expr.go
Original file line number Diff line number Diff line change
Expand Up @@ -246,11 +246,11 @@ func (e *evaluator) EvalComparisonExpr(expr *tree.ComparisonExpr) (tree.Datum, e
return ComparisonExprWithSubOperator(e.ctx(), expr, left, right)
}

_, newLeft, newRight, _, not := tree.FoldComparisonExpr(op, left, right)
_, newLeft, newRight, _, not := tree.FoldComparisonExprWithDatums(op, left, right)
if !expr.Op.CalledOnNullInput && (newLeft == tree.DNull || newRight == tree.DNull) {
return tree.DNull, nil
}
d, err := expr.Op.EvalOp.Eval(e, newLeft.(tree.Datum), newRight.(tree.Datum))
d, err := expr.Op.EvalOp.Eval(e, newLeft, newRight)
if d == tree.DNull || err != nil {
return d, err
}
Expand Down
43 changes: 43 additions & 0 deletions pkg/sql/sem/tree/compare.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,3 +55,46 @@ func FoldComparisonExpr(
}
return op, left, right, false, false
}

// FoldComparisonExprWithDatums is the same as FoldComparisonExpr, but receives
// and returns Datums instead of Exprs. This allows callers passing Datums and
// expecting Datums to be returned to avoid the expensive interface conversions.
func FoldComparisonExprWithDatums(
op treecmp.ComparisonOperator, left, right Datum,
) (newOp treecmp.ComparisonOperator, newLeft Datum, newRight Datum, flipped bool, not bool) {
switch op.Symbol {
case treecmp.NE:
// NE(left, right) is implemented as !EQ(left, right).
return treecmp.MakeComparisonOperator(treecmp.EQ), left, right, false, true
case treecmp.GT:
// GT(left, right) is implemented as LT(right, left)
return treecmp.MakeComparisonOperator(treecmp.LT), right, left, true, false
case treecmp.GE:
// GE(left, right) is implemented as LE(right, left)
return treecmp.MakeComparisonOperator(treecmp.LE), right, left, true, false
case treecmp.NotIn:
// NotIn(left, right) is implemented as !IN(left, right)
return treecmp.MakeComparisonOperator(treecmp.In), left, right, false, true
case treecmp.NotLike:
// NotLike(left, right) is implemented as !Like(left, right)
return treecmp.MakeComparisonOperator(treecmp.Like), left, right, false, true
case treecmp.NotILike:
// NotILike(left, right) is implemented as !ILike(left, right)
return treecmp.MakeComparisonOperator(treecmp.ILike), left, right, false, true
case treecmp.NotSimilarTo:
// NotSimilarTo(left, right) is implemented as !SimilarTo(left, right)
return treecmp.MakeComparisonOperator(treecmp.SimilarTo), left, right, false, true
case treecmp.NotRegMatch:
// NotRegMatch(left, right) is implemented as !RegMatch(left, right)
return treecmp.MakeComparisonOperator(treecmp.RegMatch), left, right, false, true
case treecmp.NotRegIMatch:
// NotRegIMatch(left, right) is implemented as !RegIMatch(left, right)
return treecmp.MakeComparisonOperator(treecmp.RegIMatch), left, right, false, true
case treecmp.IsDistinctFrom:
// IsDistinctFrom(left, right) is implemented as !IsNotDistinctFrom(left, right)
// Note: this seems backwards, but IS NOT DISTINCT FROM is an extended
// version of IS and IS DISTINCT FROM is an extended version of IS NOT.
return treecmp.MakeComparisonOperator(treecmp.IsNotDistinctFrom), left, right, false, true
}
return op, left, right, false, false
}

0 comments on commit 60cac1e

Please sign in to comment.