diff --git a/planner/core/BUILD.bazel b/planner/core/BUILD.bazel new file mode 100644 index 0000000000000..643509993f70f --- /dev/null +++ b/planner/core/BUILD.bazel @@ -0,0 +1,298 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "core", + srcs = [ + "access_object.go", + "collect_column_stats_usage.go", + "common_plans.go", + "debugtrace.go", + "encode.go", + "errors.go", + "exhaust_physical_plans.go", + "explain.go", + "expression_rewriter.go", + "find_best_task.go", + "flat_plan.go", + "foreign_key.go", + "fragment.go", + "handle_cols.go", + "hashcode.go", + "hints.go", + "indexmerge_path.go", + "initialize.go", + "logical_plan_builder.go", + "logical_plans.go", + "memtable_predicate_extractor.go", + "mock.go", + "optimizer.go", + "partition_prune.go", + "pb_to_plan.go", + "physical_plans.go", + "plan.go", + "plan_cache.go", + "plan_cache_lru.go", + "plan_cache_param.go", + "plan_cache_utils.go", + "plan_cacheable_checker.go", + "plan_cost_detail.go", + "plan_cost_ver1.go", + "plan_cost_ver2.go", + "plan_stats.go", + "plan_to_pb.go", + "planbuilder.go", + "point_get_plan.go", + "preprocess.go", + "property_cols_prune.go", + "resolve_indices.go", + "rule_aggregation_elimination.go", + "rule_aggregation_push_down.go", + "rule_aggregation_skew_rewrite.go", + "rule_build_key_info.go", + "rule_column_pruning.go", + "rule_decorrelate.go", + "rule_derive_topn_from_window.go", + "rule_eliminate_projection.go", + "rule_generate_column_substitute.go", + "rule_inject_extra_projection.go", + "rule_join_elimination.go", + "rule_join_reorder.go", + "rule_join_reorder_dp.go", + "rule_join_reorder_greedy.go", + "rule_max_min_eliminate.go", + "rule_partition_processor.go", + "rule_predicate_push_down.go", + "rule_predicate_simplification.go", + "rule_push_down_sequence.go", + "rule_resolve_grouping_expand.go", + "rule_result_reorder.go", + "rule_semi_join_rewrite.go", + "rule_topn_push_down.go", + "runtime_filter.go", + "runtime_filter_generator.go", + "scalar_subq_expression.go", + "show_predicate_extractor.go", + "stats.go", + "stringer.go", + "task.go", + "telemetry.go", + "tiflash_selection_late_materialization.go", + "trace.go", + "util.go", + ], + importpath = "github.com/pingcap/tidb/planner/core", + visibility = ["//visibility:public"], + deps = [ + "//bindinfo", + "//br/pkg/storage", + "//config", + "//distsql", + "//domain", + "//errno", + "//expression", + "//expression/aggregation", + "//infoschema", + "//kv", + "//lock", + "//meta/autoid", + "//metrics", + "//parser", + "//parser/ast", + "//parser/auth", + "//parser/charset", + "//parser/format", + "//parser/model", + "//parser/mysql", + "//parser/opcode", + "//parser/terror", + "//parser/types", + "//planner/cardinality", + "//planner/core/internal", + "//planner/core/internal/base", + "//planner/core/metrics", + "//planner/funcdep", + "//planner/property", + "//planner/util", + "//planner/util/debugtrace", + "//planner/util/fixcontrol", + "//privilege", + "//sessionctx", + "//sessionctx/stmtctx", + "//sessionctx/variable", + "//sessiontxn", + "//sessiontxn/staleread", + "//statistics", + "//statistics/handle/cache", + "//table", + "//table/tables", + "//table/temptable", + "//tablecodec", + "//telemetry", + "//types", + "//types/parser_driver", + "//util", + "//util/chunk", + "//util/codec", + "//util/collate", + "//util/dbterror", + "//util/dbterror/exeerrors", + "//util/disjointset", + "//util/domainutil", + "//util/execdetails", + "//util/filter", + "//util/hack", + "//util/hint", + "//util/intest", + "//util/kvcache", + "//util/logutil", + "//util/mathutil", + "//util/memory", + "//util/mock", + "//util/paging", + "//util/parser", + "//util/plancache", + "//util/plancodec", + "//util/ranger", + "//util/rowcodec", + "//util/sem", + "//util/set", + "//util/size", + "//util/sqlexec", + "//util/stmtsummary", + "//util/stringutil", + "//util/syncutil", + "//util/texttree", + "//util/tiflashcompute", + "//util/tracing", + "@com_github_pingcap_errors//:errors", + "@com_github_pingcap_failpoint//:failpoint", + "@com_github_pingcap_kvproto//pkg/coprocessor", + "@com_github_pingcap_kvproto//pkg/diagnosticspb", + "@com_github_pingcap_tipb//go-tipb", + "@com_github_tikv_client_go_v2//kv", + "@com_github_tikv_client_go_v2//oracle", + "@com_github_tikv_client_go_v2//tikv", + "@org_uber_go_atomic//:atomic", + "@org_uber_go_zap//:zap", + ], +) + +go_test( + name = "core_test", + timeout = "short", + srcs = [ + "binary_plan_test.go", + "cbo_test.go", + "collect_column_stats_usage_test.go", + "common_plans_test.go", + "enforce_mpp_test.go", + "errors_test.go", + "exhaust_physical_plans_test.go", + "expression_rewriter_test.go", + "expression_test.go", + "find_best_task_test.go", + "fragment_test.go", + "indexmerge_intersection_test.go", + "indexmerge_path_test.go", + "indexmerge_test.go", + "integration_partition_test.go", + "integration_test.go", + "logical_plan_trace_test.go", + "logical_plans_test.go", + "main_test.go", + "memtable_predicate_extractor_test.go", + "optimizer_test.go", + "partition_pruner_test.go", + "partition_pruning_test.go", + "physical_plan_test.go", + "physical_plan_trace_test.go", + "plan_cache_lru_test.go", + "plan_cache_param_test.go", + "plan_cache_test.go", + "plan_cache_utils_test.go", + "plan_cacheable_checker_test.go", + "plan_cost_detail_test.go", + "plan_cost_ver1_test.go", + "plan_cost_ver2_test.go", + "plan_replayer_capture_test.go", + "plan_stats_test.go", + "plan_test.go", + "plan_to_pb_test.go", + "planbuilder_test.go", + "point_get_plan_test.go", + "preprocess_test.go", + "rule_generate_column_substitute_test.go", + "rule_join_reorder_dp_test.go", + "rule_join_reorder_test.go", + "rule_result_reorder_test.go", + "runtime_filter_generator_test.go", + "stringer_test.go", + ], + data = glob(["testdata/**"]), + embed = [":core"], + flaky = True, + shard_count = 50, + deps = [ + "//config", + "//domain", + "//errno", + "//executor", + "//expression", + "//expression/aggregation", + "//infoschema", + "//kv", + "//metrics", + "//parser", + "//parser/ast", + "//parser/auth", + "//parser/charset", + "//parser/format", + "//parser/model", + "//parser/mysql", + "//parser/terror", + "//planner", + "//planner/core/internal", + "//planner/property", + "//planner/util", + "//session", + "//sessionctx", + "//sessionctx/stmtctx", + "//sessionctx/variable", + "//sessiontxn", + "//statistics", + "//statistics/handle", + "//table", + "//testkit", + "//testkit/ddlhelper", + "//testkit/external", + "//testkit/testdata", + "//testkit/testmain", + "//testkit/testsetup", + "//testkit/testutil", + "//types", + "//types/parser_driver", + "//util", + "//util/benchdaily", + "//util/collate", + "//util/dbterror", + "//util/hack", + "//util/hint", + "//util/kvcache", + "//util/logutil", + "//util/mock", + "//util/plancache", + "//util/plancodec", + "//util/ranger", + "//util/set", + "//util/size", + "//util/stmtsummary", + "//util/tracing", + "@com_github_golang_snappy//:snappy", + "@com_github_pingcap_errors//:errors", + "@com_github_pingcap_failpoint//:failpoint", + "@com_github_pingcap_tipb//go-tipb", + "@com_github_prometheus_client_model//go", + "@com_github_stretchr_testify//require", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/planner/core/logical_plans_test.go b/planner/core/logical_plans_test.go index 69db1e9f8a839..2524e885c0647 100644 --- a/planner/core/logical_plans_test.go +++ b/planner/core/logical_plans_test.go @@ -36,7 +36,56 @@ type testUnitTestSuit struct { ctx sessionctx.Context } +<<<<<<< HEAD func (s *testUnitTestSuit) SetUpSuite(c *C) { +======= +func (p *plannerSuite) GetParser() *parser.Parser { + return p.p +} + +func (p *plannerSuite) GetIS() infoschema.InfoSchema { + return p.is +} + +func (p *plannerSuite) GetCtx() sessionctx.Context { + return p.ctx +} + +func CreatePlannerSuite(sctx sessionctx.Context, is infoschema.InfoSchema) (s *plannerSuite) { + s = new(plannerSuite) + s.is = is + s.p = parser.New() + s.ctx = sctx + return s +} + +func createPlannerSuite() (s *plannerSuite) { + s = new(plannerSuite) + tblInfos := []*model.TableInfo{ + MockSignedTable(), + MockUnsignedTable(), + MockView(), + MockNoPKTable(), + MockRangePartitionTable(), + MockHashPartitionTable(), + MockListPartitionTable(), + MockStateNoneColumnTable(), + } + id := int64(0) + for _, tblInfo := range tblInfos { + tblInfo.ID = id + id += 1 + pi := tblInfo.GetPartitionInfo() + if pi == nil { + continue + } + for _, def := range pi.Definitions { + def.ID = id + id += 1 + } + } + s.is = infoschema.MockInfoSchema(tblInfos) +>>>>>>> 41c16424ac0 (planner: don't recompute the hashcode when generated column substitution doesn't happen (#46450)) s.ctx = MockContext() } diff --git a/planner/core/rule_generate_column_substitute.go b/planner/core/rule_generate_column_substitute.go index d3ec3d5c7960c..9cdd0cf3955aa 100644 --- a/planner/core/rule_generate_column_substitute.go +++ b/planner/core/rule_generate_column_substitute.go @@ -74,32 +74,83 @@ func collectGenerateColumn(lp LogicalPlan, exprToColumn ExprColumnMap) { } } +<<<<<<< HEAD func tryToSubstituteExpr(expr *expression.Expression, sctx sessionctx.Context, candidateExpr expression.Expression, tp types.EvalType, schema *expression.Schema, col *expression.Column) { if (*expr).Equal(sctx, candidateExpr) && candidateExpr.GetType().EvalType() == tp && schema.ColumnIndex(col) != -1 { *expr = col +======= +func tryToSubstituteExpr(expr *expression.Expression, lp LogicalPlan, candidateExpr expression.Expression, tp types.EvalType, schema *expression.Schema, col *expression.Column, opt *logicalOptimizeOp) bool { + changed := false + if (*expr).Equal(lp.SCtx(), candidateExpr) && candidateExpr.GetType().EvalType() == tp && + schema.ColumnIndex(col) != -1 { + *expr = col + appendSubstituteColumnStep(lp, candidateExpr, col, opt) + changed = true +>>>>>>> 41c16424ac0 (planner: don't recompute the hashcode when generated column substitution doesn't happen (#46450)) } + return changed } +<<<<<<< HEAD func substituteExpression(cond expression.Expression, sctx *stmtctx.StatementContext, sessionCtx sessionctx.Context, exprToColumn ExprColumnMap, schema *expression.Schema) { +======= +func appendSubstituteColumnStep(lp LogicalPlan, candidateExpr expression.Expression, col *expression.Column, opt *logicalOptimizeOp) { + reason := func() string { return "" } + action := func() string { + buffer := bytes.NewBufferString("expression:") + buffer.WriteString(candidateExpr.String()) + buffer.WriteString(" substituted by") + buffer.WriteString(" column:") + buffer.WriteString(col.String()) + return buffer.String() + } + opt.appendStepToCurrent(lp.ID(), lp.TP(), reason, action) +} + +// SubstituteExpression is Exported for bench +func SubstituteExpression(cond expression.Expression, lp LogicalPlan, exprToColumn ExprColumnMap, schema *expression.Schema, opt *logicalOptimizeOp) bool { + return substituteExpression(cond, lp, exprToColumn, schema, opt) +} + +func substituteExpression(cond expression.Expression, lp LogicalPlan, exprToColumn ExprColumnMap, schema *expression.Schema, opt *logicalOptimizeOp) bool { +>>>>>>> 41c16424ac0 (planner: don't recompute the hashcode when generated column substitution doesn't happen (#46450)) sf, ok := cond.(*expression.ScalarFunction) if !ok { - return + return false } +<<<<<<< HEAD +======= + sctx := lp.SCtx().GetSessionVars().StmtCtx + changed := false + collectChanged := func(partial bool) { + if partial && !changed { + changed = true + } + } +>>>>>>> 41c16424ac0 (planner: don't recompute the hashcode when generated column substitution doesn't happen (#46450)) defer func() { // If the argument is not changed, hash code doesn't need to recount again. - // But we always do it to keep the code simple and stupid. - expression.ReHashCode(sf, sctx) + if changed { + expression.ReHashCode(sf, sctx) + } }() var expr *expression.Expression var tp types.EvalType switch sf.FuncName.L { case ast.EQ, ast.LT, ast.LE, ast.GT, ast.GE: for candidateExpr, column := range exprToColumn { +<<<<<<< HEAD tryToSubstituteExpr(&sf.GetArgs()[1], sessionCtx, candidateExpr, sf.GetArgs()[0].GetType().EvalType(), schema, column) } for candidateExpr, column := range exprToColumn { tryToSubstituteExpr(&sf.GetArgs()[0], sessionCtx, candidateExpr, sf.GetArgs()[1].GetType().EvalType(), schema, column) +======= + collectChanged(tryToSubstituteExpr(&sf.GetArgs()[1], lp, candidateExpr, sf.GetArgs()[0].GetType().EvalType(), schema, column, opt)) + } + for candidateExpr, column := range exprToColumn { + collectChanged(tryToSubstituteExpr(&sf.GetArgs()[0], lp, candidateExpr, sf.GetArgs()[1].GetType().EvalType(), schema, column, opt)) +>>>>>>> 41c16424ac0 (planner: don't recompute the hashcode when generated column substitution doesn't happen (#46450)) } case ast.In: expr = &sf.GetArgs()[0] @@ -115,13 +166,18 @@ func substituteExpression(cond expression.Expression, sctx *stmtctx.StatementCon } if canSubstitute { for candidateExpr, column := range exprToColumn { +<<<<<<< HEAD tryToSubstituteExpr(expr, sessionCtx, candidateExpr, tp, schema, column) +======= + collectChanged(tryToSubstituteExpr(expr, lp, candidateExpr, tp, schema, column, opt)) +>>>>>>> 41c16424ac0 (planner: don't recompute the hashcode when generated column substitution doesn't happen (#46450)) } } case ast.Like: expr = &sf.GetArgs()[0] tp = sf.GetArgs()[1].GetType().EvalType() for candidateExpr, column := range exprToColumn { +<<<<<<< HEAD tryToSubstituteExpr(expr, sessionCtx, candidateExpr, tp, schema, column) } case ast.LogicOr, ast.LogicAnd: @@ -129,7 +185,17 @@ func substituteExpression(cond expression.Expression, sctx *stmtctx.StatementCon substituteExpression(sf.GetArgs()[1], sctx, sessionCtx, exprToColumn, schema) case ast.UnaryNot: substituteExpression(sf.GetArgs()[0], sctx, sessionCtx, exprToColumn, schema) +======= + collectChanged(tryToSubstituteExpr(expr, lp, candidateExpr, tp, schema, column, opt)) + } + case ast.LogicOr, ast.LogicAnd: + collectChanged(substituteExpression(sf.GetArgs()[0], lp, exprToColumn, schema, opt)) + collectChanged(substituteExpression(sf.GetArgs()[1], lp, exprToColumn, schema, opt)) + case ast.UnaryNot: + collectChanged(substituteExpression(sf.GetArgs()[0], lp, exprToColumn, schema, opt)) +>>>>>>> 41c16424ac0 (planner: don't recompute the hashcode when generated column substitution doesn't happen (#46450)) } + return changed } func (gc *gcSubstituter) substitute(ctx context.Context, lp LogicalPlan, exprToColumn ExprColumnMap) LogicalPlan { diff --git a/planner/core/rule_generate_column_substitute_test.go b/planner/core/rule_generate_column_substitute_test.go new file mode 100644 index 0000000000000..ece8c6915f9a2 --- /dev/null +++ b/planner/core/rule_generate_column_substitute_test.go @@ -0,0 +1,280 @@ +// Copyright 2023 PingCAP, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package core_test + +import ( + "context" + "fmt" + "testing" + + "github.com/pingcap/tidb/domain" + "github.com/pingcap/tidb/parser/model" + "github.com/pingcap/tidb/planner/core" + "github.com/pingcap/tidb/testkit" + "github.com/stretchr/testify/require" +) + +// ➜ core git:(master) ✗ go tool pprof m5.file +// File: ___5BenchmarkSubstituteExpression_in_github_com_pingcap_tidb_planner_core.test +// Build ID: ea7c603fe0cf5e18deac5bf65d36f115467fce80 +// Type: alloc_space +// Time: Aug 28, 2023 at 3:58pm (CST) +// Entering interactive mode (type "help" for commands, "o" for options) +// (pprof) list BenchmarkSubstituteExpression +// Total: 1.40GB +// ROUTINE ======================== github.com/pingcap/tidb/planner/core_test.BenchmarkSubstituteExpression in /home/arenatlx/go/src/github.com/pingcap/tidb/planner/core/rule_generate_column_substitute_test.go +// +// 0 173.44MB (flat, cum) 12.12% of Total +// . . 29:func BenchmarkSubstituteExpression(b *testing.B) { +// . 169.91MB 30: store := testkit.CreateMockStore(b) +// . . 31: tk := testkit.NewTestKit(b, store) +// . . 32: tk.MustExec("use test") +// . . 33: tk.MustExec("drop table if exists tai") +// . 512.19kB 34: tk.MustExec("create table tai(a varchar(256), b varchar(256), c int as (a+1), d int as (b+1))") +// . . 35: is := domain.GetDomain(tk.Session()).InfoSchema() +// . . 36: _, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("tai")) +// . . 37: require.NoError(b, err) +// . . 38: condition := "(tai.a='%s' AND tai.b='%s') OR" + +// . . 39: "(tai.a='%s' AND tai.b='%s') OR" + +// . . 40: "(tai.a='%s' AND tai.b='%s') OR" + +// . . 41: "(tai.a='%s' AND tai.b='%s') OR" + +// . . 42: "(tai.a='%s' AND tai.b='%s') OR" + +// . . 43: "(tai.a='%s' AND tai.b='%s') OR" + +// . . 44: "(tai.a='%s' AND tai.b='%s') OR" + +// . . 45: "(tai.a='%s' AND tai.b='%s') OR" + +// . . 46: "(tai.a='%s' AND tai.b='%s') OR" + +// . . 47: "(tai.a='%s' AND tai.b='%s') OR" + +// . . 48: "(tai.a='%s' AND tai.b='%s') OR" + +// . . 49: "(tai.a='%s' AND tai.b='%s') OR" + +// . . 50: "(tai.a='%s' AND tai.b='%s') OR" + +// . . 51: "(tai.a='%s' AND tai.b='%s') OR" + +// . . 52: "(tai.a='%s' AND tai.b='%s') OR" + +// . . 53: "(tai.a='%s' AND tai.b='%s') OR" + +// . . 54: "(tai.a='%s' AND tai.b='%s') OR" + +// . . 55: "(tai.a='%s' AND tai.b='%s') OR" + +// . . 56: "(tai.a='%s' AND tai.b='%s') OR" + +// . . 57: "(tai.a='%s' AND tai.b='%s') OR" + +// . . 58: "(tai.a='%s' AND tai.b='%s') OR" + +// . . 59: "(tai.a='%s' AND tai.b='%s') OR" + +// . . 60: "(tai.a='%s' AND tai.b='%s') OR" + +// . . 61: "(tai.a='%s' AND tai.b='%s') OR" + +// . . 62: "(tai.a='%s' AND tai.b='%s') OR" + +// . . 63: "(tai.a='%s' AND tai.b='%s') OR" + +// . . 64: "(tai.a='%s' AND tai.b='%s') OR" + +// . . 65: "(tai.a='%s' AND tai.b='%s') OR" + +// . . 66: "(tai.a='%s' AND tai.b='%s') OR" + +// . . 67: "(tai.a='%s' AND tai.b='%s') OR" + +// . . 68: "(tai.a='%s' AND tai.b='%s') OR" + +// . . 69: "(tai.a='%s' AND tai.b='%s') OR" + +// . . 70: "(tai.a='%s' AND tai.b='%s') OR" + +// . . 71: "(tai.a='%s' AND tai.b='%s') OR" + +// . . 72: "(tai.a='%s' AND tai.b='%s') OR" + +// . . 73: "(tai.a='%s' AND tai.b='%s') OR" + +// . . 74: "(tai.a='%s' AND tai.b='%s') OR" + +// . . 75: "(tai.a='%s' AND tai.b='%s') OR" + +// . . 76: "(tai.a='%s' AND tai.b='%s') OR" + +// . . 77: "(tai.a='%s' AND tai.b='%s')" +// . . 78: addresses := make([]interface{}, 0, 90) +// . . 79: for i := 0; i < 80; i++ { +// . . 80: addresses = append(addresses, "0x6ab6Bf9117A8A9dd5a2FF203aa8a22457162fC510x6ab6Bf9117A8A9dd5a2FF203aa8a22457162fC510x6ab6Bf9117A8A9dd5a2FF203aa8a22457162fC510x6ab6Bf9117A8A9dd5a2FF203aa8a22457162fC51") +// . . 81: } +// . 520.04kB 82: condition = fmt.Sprintf(condition, addresses...) +// . . 83: s := core.CreatePlannerSuite(tk.Session(), is) +// . . 84: ctx := context.Background() +// . . 85: sql := "select * from tai where " + condition +// . . 86: fmt.Println(sql) +// . . 87: stmt, err := s.GetParser().ParseOneStmt(sql, "", "") +// . . 88: require.NoError(b, err, sql) +// . 512.01kB 89: p, _, err := core.BuildLogicalPlanForTest(ctx, s.GetCtx(), stmt, s.GetIS()) +// . . 90: require.NoError(b, err) +// . . 91: selection := p.(core.LogicalPlan).Children()[0] +// . . 92: m := make(core.ExprColumnMap, len(selection.Schema().Columns)) +// . . 93: for _, col := range selection.Schema().Columns { +// . . 94: if col.VirtualExpr != nil { +// . . 95: m[col.VirtualExpr] = col +// . . 96: } +// . . 97: } +// . . 98: b.ResetTimer() +// . . 99: b.StartTimer() +// . . 100: for i := 0; i < b.N; i++ { +// . 2.02MB 101: core.SubstituteExpression(selection.(*core.LogicalSelection).Conditions[0], selection, m, selection.Schema(), nil) +// . . 102: } +// . . 103: b.StopTimer() +// . . 104:} +// +// **************************************************************************************************************************************************************** +// after this patch +// ➜ core git:(fix-42788) ✗ go tool pprof m5.file +// File: ___5BenchmarkSubstituteExpression_in_github_com_pingcap_tidb_planner_core.test +// Build ID: dce9437cc5156c542bc642092b25b29de9b14d87 +// Type: alloc_space +// Time: Aug 28, 2023 at 4:04pm (CST) +// Entering interactive mode (type "help" for commands, "o" for options) +// (pprof) list BenchmarkSubstituteExpression +// Total: 1.41GB +// ROUTINE ======================== github.com/pingcap/tidb/planner/core_test.BenchmarkSubstituteExpression in /home/arenatlx/go/src/github.com/pingcap/tidb/planner/core/rule_generate_column_substitute_test.go +// +// 0 172.22MB (flat, cum) 11.90% of Total +// . . 29:func BenchmarkSubstituteExpression(b *testing.B) { +// . 170.21MB 30: store := testkit.CreateMockStore(b) +// . 1.01MB 31: tk := testkit.NewTestKit(b, store) +// . . 32: tk.MustExec("use test") +// . . 33: tk.MustExec("drop table if exists tai") +// . . 34: tk.MustExec("create table tai(a varchar(256), b varchar(256), c int as (a+1), d int as (b+1))") +// . . 35: is := domain.GetDomain(tk.Session()).InfoSchema() +// . . 36: _, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("tai")) +// . . 37: require.NoError(b, err) +// . . 38: condition := "(tai.a='%s' AND tai.b='%s') OR" + +// . . 39: "(tai.a='%s' AND tai.b='%s') OR" + +// . . 40: "(tai.a='%s' AND tai.b='%s') OR" + +// . . 41: "(tai.a='%s' AND tai.b='%s') OR" + +// . . 42: "(tai.a='%s' AND tai.b='%s') OR" + +// . . 43: "(tai.a='%s' AND tai.b='%s') OR" + +// . . 44: "(tai.a='%s' AND tai.b='%s') OR" + +// . . 45: "(tai.a='%s' AND tai.b='%s') OR" + +// . . 46: "(tai.a='%s' AND tai.b='%s') OR" + +// . . 47: "(tai.a='%s' AND tai.b='%s') OR" + +// . . 48: "(tai.a='%s' AND tai.b='%s') OR" + +// . . 49: "(tai.a='%s' AND tai.b='%s') OR" + +// . . 50: "(tai.a='%s' AND tai.b='%s') OR" + +// . . 51: "(tai.a='%s' AND tai.b='%s') OR" + +// . . 52: "(tai.a='%s' AND tai.b='%s') OR" + +// . . 53: "(tai.a='%s' AND tai.b='%s') OR" + +// . . 54: "(tai.a='%s' AND tai.b='%s') OR" + +// . . 55: "(tai.a='%s' AND tai.b='%s') OR" + +// . . 56: "(tai.a='%s' AND tai.b='%s') OR" + +// . . 57: "(tai.a='%s' AND tai.b='%s') OR" + +// . . 58: "(tai.a='%s' AND tai.b='%s') OR" + +// . . 59: "(tai.a='%s' AND tai.b='%s') OR" + +// . . 60: "(tai.a='%s' AND tai.b='%s') OR" + +// . . 61: "(tai.a='%s' AND tai.b='%s') OR" + +// . . 62: "(tai.a='%s' AND tai.b='%s') OR" + +// . . 63: "(tai.a='%s' AND tai.b='%s') OR" + +// . . 64: "(tai.a='%s' AND tai.b='%s') OR" + +// . . 65: "(tai.a='%s' AND tai.b='%s') OR" + +// . . 66: "(tai.a='%s' AND tai.b='%s') OR" + +// . . 67: "(tai.a='%s' AND tai.b='%s') OR" + +// . . 68: "(tai.a='%s' AND tai.b='%s') OR" + +// . . 69: "(tai.a='%s' AND tai.b='%s') OR" + +// . . 70: "(tai.a='%s' AND tai.b='%s') OR" + +// . . 71: "(tai.a='%s' AND tai.b='%s') OR" + +// . . 72: "(tai.a='%s' AND tai.b='%s') OR" + +// . . 73: "(tai.a='%s' AND tai.b='%s') OR" + +// . . 74: "(tai.a='%s' AND tai.b='%s') OR" + +// . . 75: "(tai.a='%s' AND tai.b='%s') OR" + +// . . 76: "(tai.a='%s' AND tai.b='%s') OR" + +// . . 77: "(tai.a='%s' AND tai.b='%s')q" +// . . 78: addresses := make([]interface{}, 0, 90) +// . . 79: for i := 0; i < 80; i++ { +// . . 80: addresses = append(addresses, "0x6ab6Bf9117A8A9dd5a2FF203aa8a22457162fC510x6ab6Bf9117A8A9dd5a2FF203aa8a22457162fC510x6ab6Bf9117A8A9dd5a2FF203aa8a22457162fC510x6ab6Bf9117A8A9dd5a2FF203aa8a22457162fC51") +// . . 81: } +// . . 82: condition = fmt.Sprintf(condition, addresses...) +// . 520.04kB 83: s := core.CreatePlannerSuite(tk.Session(), is) +// . . 84: ctx := context.Background() +// . . 85: sql := "select * from tai where " + condition +// . . 86: fmt.Println(sql) +// . . 87: stmt, err := s.GetParser().ParseOneStmt(sql, "", "") +// . . 88: require.NoError(b, err, sql) +// . 512.07kB 89: p, _, err := core.BuildLogicalPlanForTest(ctx, s.GetCtx(), stmt, s.GetIS()) +// . . 90: require.NoError(b, err) +// . . 91: selection := p.(core.LogicalPlan).Children()[0] +// . . 92: m := make(core.ExprColumnMap, len(selection.Schema().Columns)) +// . . 93: for _, col := range selection.Schema().Columns { +// . . 94: if col.VirtualExpr != nil { +// +// (pprof) +// (pprof) q +// +// We couldn't see any allocation around core.SubstituteExpression after this patch is applied when there is no generated expression substitution happened +// in the recursive dfs down. In the real environment, the contract hash address will be more complicated and embedding layer will be deeper, causing +// the more memory consumption rather than just a few 2MB as shown above. +// +// Expression hashcode is a lazy utility used for comparison in some cases, if the later usage is not exist, the re-computation of them here is also unnecessary. +func BenchmarkSubstituteExpression(b *testing.B) { + store := testkit.CreateMockStore(b) + tk := testkit.NewTestKit(b, store) + tk.MustExec("use test") + tk.MustExec("drop table if exists tai") + tk.MustExec("create table tai(a varchar(256), b varchar(256), c int as (a+1), d int as (b+1))") + is := domain.GetDomain(tk.Session()).InfoSchema() + _, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("tai")) + require.NoError(b, err) + condition := "(tai.a='%s' AND tai.b='%s') OR" + + "(tai.a='%s' AND tai.b='%s') OR" + + "(tai.a='%s' AND tai.b='%s') OR" + + "(tai.a='%s' AND tai.b='%s') OR" + + "(tai.a='%s' AND tai.b='%s') OR" + + "(tai.a='%s' AND tai.b='%s') OR" + + "(tai.a='%s' AND tai.b='%s') OR" + + "(tai.a='%s' AND tai.b='%s') OR" + + "(tai.a='%s' AND tai.b='%s') OR" + + "(tai.a='%s' AND tai.b='%s') OR" + + "(tai.a='%s' AND tai.b='%s') OR" + + "(tai.a='%s' AND tai.b='%s') OR" + + "(tai.a='%s' AND tai.b='%s') OR" + + "(tai.a='%s' AND tai.b='%s') OR" + + "(tai.a='%s' AND tai.b='%s') OR" + + "(tai.a='%s' AND tai.b='%s') OR" + + "(tai.a='%s' AND tai.b='%s') OR" + + "(tai.a='%s' AND tai.b='%s') OR" + + "(tai.a='%s' AND tai.b='%s') OR" + + "(tai.a='%s' AND tai.b='%s') OR" + + "(tai.a='%s' AND tai.b='%s') OR" + + "(tai.a='%s' AND tai.b='%s') OR" + + "(tai.a='%s' AND tai.b='%s') OR" + + "(tai.a='%s' AND tai.b='%s') OR" + + "(tai.a='%s' AND tai.b='%s') OR" + + "(tai.a='%s' AND tai.b='%s') OR" + + "(tai.a='%s' AND tai.b='%s') OR" + + "(tai.a='%s' AND tai.b='%s') OR" + + "(tai.a='%s' AND tai.b='%s') OR" + + "(tai.a='%s' AND tai.b='%s') OR" + + "(tai.a='%s' AND tai.b='%s') OR" + + "(tai.a='%s' AND tai.b='%s') OR" + + "(tai.a='%s' AND tai.b='%s') OR" + + "(tai.a='%s' AND tai.b='%s') OR" + + "(tai.a='%s' AND tai.b='%s') OR" + + "(tai.a='%s' AND tai.b='%s') OR" + + "(tai.a='%s' AND tai.b='%s') OR" + + "(tai.a='%s' AND tai.b='%s') OR" + + "(tai.a='%s' AND tai.b='%s') OR" + + "(tai.a='%s' AND tai.b='%s')" + addresses := make([]interface{}, 0, 90) + for i := 0; i < 80; i++ { + addresses = append(addresses, "0x6ab6Bf9117A8A9dd5a2FF203aa8a22457162fC510x6ab6Bf9117A8A9dd5a2FF203aa8a22457162fC510x6ab6Bf9117A8A9dd5a2FF203aa8a22457162fC510x6ab6Bf9117A8A9dd5a2FF203aa8a22457162fC51") + } + condition = fmt.Sprintf(condition, addresses...) + s := core.CreatePlannerSuite(tk.Session(), is) + ctx := context.Background() + sql := "select * from tai where " + condition + fmt.Println(sql) + stmt, err := s.GetParser().ParseOneStmt(sql, "", "") + require.NoError(b, err, sql) + p, _, err := core.BuildLogicalPlanForTest(ctx, s.GetCtx(), stmt, s.GetIS()) + require.NoError(b, err) + selection := p.(core.LogicalPlan).Children()[0] + m := make(core.ExprColumnMap, len(selection.Schema().Columns)) + for _, col := range selection.Schema().Columns { + if col.VirtualExpr != nil { + m[col.VirtualExpr] = col + } + } + b.ResetTimer() + b.StartTimer() + for i := 0; i < b.N; i++ { + core.SubstituteExpression(selection.(*core.LogicalSelection).Conditions[0], selection, m, selection.Schema(), nil) + } + b.StopTimer() +}