diff --git a/distsql/distsql_test.go b/distsql/distsql_test.go index 41e20258152c4..d361f9ddf1ad0 100644 --- a/distsql/distsql_test.go +++ b/distsql/distsql_test.go @@ -167,7 +167,7 @@ func (s *testSuite) TestSelectResultRuntimeStats(c *C) { rpcStat: tikv.NewRegionRequestRuntimeStats(), } s2 := *s1 - stmtStats := execdetails.NewRuntimeStatsColl() + stmtStats := execdetails.NewRuntimeStatsColl(nil) stmtStats.RegisterStats(1, basic) stmtStats.RegisterStats(1, s1) stmtStats.RegisterStats(1, &s2) diff --git a/distsql/select_result_test.go b/distsql/select_result_test.go index 2046f53c51695..cecf231d8f2a1 100644 --- a/distsql/select_result_test.go +++ b/distsql/select_result_test.go @@ -33,7 +33,7 @@ func (s *testSuite) TestUpdateCopRuntimeStats(c *C) { sr.rootPlanID = 1234 sr.updateCopRuntimeStats(context.Background(), &copr.CopRuntimeStats{ExecDetails: execdetails.ExecDetails{CalleeAddress: "a"}}, 0) - ctx.GetSessionVars().StmtCtx.RuntimeStatsColl = execdetails.NewRuntimeStatsColl() + ctx.GetSessionVars().StmtCtx.RuntimeStatsColl = execdetails.NewRuntimeStatsColl(nil) t := uint64(1) sr.selectResp = &tipb.SelectResponse{ ExecutionSummaries: []*tipb.ExecutorExecutionSummary{ diff --git a/executor/builder.go b/executor/builder.go index b8d820d2284c2..43555a67c854f 100644 --- a/executor/builder.go +++ b/executor/builder.go @@ -970,7 +970,7 @@ func (b *executorBuilder) buildExplain(v *plannercore.Explain) Executor { } if v.Analyze { if b.ctx.GetSessionVars().StmtCtx.RuntimeStatsColl == nil { - b.ctx.GetSessionVars().StmtCtx.RuntimeStatsColl = execdetails.NewRuntimeStatsColl() + b.ctx.GetSessionVars().StmtCtx.RuntimeStatsColl = execdetails.NewRuntimeStatsColl(nil) } explainExec.analyzeExec = b.build(v.TargetPlan) } diff --git a/executor/executor.go b/executor/executor.go index 7569830f3f870..297b6a8e66dc2 100644 --- a/executor/executor.go +++ b/executor/executor.go @@ -1808,7 +1808,13 @@ func ResetContextOfStmt(ctx sessionctx.Context, s ast.StmtNode) (err error) { sc.PrevAffectedRows = -1 } if globalConfig.EnableCollectExecutionInfo { - sc.RuntimeStatsColl = execdetails.NewRuntimeStatsColl() + // In ExplainFor case, RuntimeStatsColl should not be reset for reuse, + // because ExplainFor need to display the last statement information. + reuseObj := vars.StmtCtx.RuntimeStatsColl + if _, ok := s.(*ast.ExplainForStmt); ok { + reuseObj = nil + } + sc.RuntimeStatsColl = execdetails.NewRuntimeStatsColl(reuseObj) } sc.TblInfo2UnionScan = make(map[*model.TableInfo]bool) diff --git a/util/execdetails/execdetails.go b/util/execdetails/execdetails.go index 68d1d9656caef..42f5c270f5030 100644 --- a/util/execdetails/execdetails.go +++ b/util/execdetails/execdetails.go @@ -555,9 +555,23 @@ type RuntimeStatsColl struct { } // NewRuntimeStatsColl creates new executor collector. -func NewRuntimeStatsColl() *RuntimeStatsColl { - return &RuntimeStatsColl{rootStats: make(map[int]*RootRuntimeStats), - copStats: make(map[int]*CopRuntimeStats)} +// Reuse the object to reduce allocation when *RuntimeStatsColl is not nil. +func NewRuntimeStatsColl(reuse *RuntimeStatsColl) *RuntimeStatsColl { + if reuse != nil { + // Reuse map is cheaper than create a new map object. + // Go compiler optimize this cleanup code pattern to a clearmap() function. + for k := range reuse.rootStats { + delete(reuse.rootStats, k) + } + for k := range reuse.copStats { + delete(reuse.copStats, k) + } + return reuse + } + return &RuntimeStatsColl{ + rootStats: make(map[int]*RootRuntimeStats), + copStats: make(map[int]*CopRuntimeStats), + } } // RegisterStats register execStat for a executor. diff --git a/util/execdetails/execdetails_test.go b/util/execdetails/execdetails_test.go index 6bfcfbeca703e..9ad39abe390d1 100644 --- a/util/execdetails/execdetails_test.go +++ b/util/execdetails/execdetails_test.go @@ -93,7 +93,7 @@ func mockExecutorExecutionSummaryForTiFlash(TimeProcessedNs, NumProducedRows, Nu } func TestCopRuntimeStats(t *testing.T) { - stats := NewRuntimeStatsColl() + stats := NewRuntimeStatsColl(nil) tableScanID := 1 aggID := 2 tableReaderID := 3 @@ -156,7 +156,7 @@ func TestCopRuntimeStats(t *testing.T) { } func TestCopRuntimeStatsForTiFlash(t *testing.T) { - stats := NewRuntimeStatsColl() + stats := NewRuntimeStatsColl(nil) tableScanID := 1 aggID := 2 tableReaderID := 3 @@ -261,7 +261,7 @@ func TestRootRuntimeStats(t *testing.T) { basic1.Record(time.Second, 20) basic2.Record(time.Second*2, 30) pid := 1 - stmtStats := NewRuntimeStatsColl() + stmtStats := NewRuntimeStatsColl(nil) stmtStats.RegisterStats(pid, basic1) stmtStats.RegisterStats(pid, basic2) concurrency := &RuntimeStatsWithConcurrencyInfo{}