diff --git a/planner/core/common_plans.go b/planner/core/common_plans.go index 988f4f54c8d72..648042eaf7d7a 100644 --- a/planner/core/common_plans.go +++ b/planner/core/common_plans.go @@ -38,6 +38,7 @@ import ( "github.com/pingcap/tidb/util/hint" "github.com/pingcap/tidb/util/memory" "github.com/pingcap/tidb/util/plancodec" + "github.com/pingcap/tidb/util/size" "github.com/pingcap/tidb/util/texttree" "github.com/pingcap/tipb/go-tipb" ) @@ -280,6 +281,16 @@ type Simple struct { StaleTxnStartTS uint64 } +// MemoryUsage return the memory usage of Simple +func (s *Simple) MemoryUsage() (sum int64) { + if s == nil { + return + } + + sum = s.baseSchemaProducer.MemoryUsage() + size.SizeOfInterface + size.SizeOfBool + size.SizeOfUint64 + return +} + // PhysicalSimpleWrapper is a wrapper of `Simple` to implement physical plan interface. // // Used for simple statements executing in coprocessor. @@ -288,6 +299,16 @@ type PhysicalSimpleWrapper struct { Inner Simple } +// MemoryUsage return the memory usage of PhysicalSimpleWrapper +func (p *PhysicalSimpleWrapper) MemoryUsage() (sum int64) { + if p == nil { + return + } + + sum = p.basePhysicalPlan.MemoryUsage() + p.Inner.MemoryUsage() + return +} + // InsertGeneratedColumns is for completing generated columns in Insert. // We resolve generation expressions in plan, and eval those in executor. type InsertGeneratedColumns struct { diff --git a/planner/core/fragment.go b/planner/core/fragment.go index ec5785404757f..5dfa93186826f 100644 --- a/planner/core/fragment.go +++ b/planner/core/fragment.go @@ -17,6 +17,7 @@ package core import ( "context" "time" + "unsafe" "github.com/pingcap/errors" "github.com/pingcap/tidb/distsql" @@ -29,6 +30,7 @@ import ( "github.com/pingcap/tidb/types" "github.com/pingcap/tidb/util/logutil" "github.com/pingcap/tidb/util/ranger" + "github.com/pingcap/tidb/util/size" "github.com/pingcap/tipb/go-tipb" "go.uber.org/zap" "golang.org/x/exp/slices" @@ -49,6 +51,28 @@ type Fragment struct { singleton bool // indicates if this is a task running on a single node. } +const emptyFragmentSize = int64(unsafe.Sizeof(Fragment{})) + +// MemoryUsage return the memory usage of Fragment +func (f *Fragment) MemoryUsage() (sum int64) { + if f == nil { + return + } + + sum = emptyFragmentSize + int64(cap(f.ExchangeReceivers))*size.SizeOfPointer + if f.TableScan != nil { + sum += f.TableScan.MemoryUsage() + } + if f.ExchangeSender != nil { + sum += f.ExchangeSender.MemoryUsage() + } + + for _, receiver := range f.ExchangeReceivers { + sum += receiver.MemoryUsage() + } + return +} + type tasksAndFrags struct { tasks []*kv.MPPTask frags []*Fragment diff --git a/planner/core/physical_plans.go b/planner/core/physical_plans.go index 976660cd21769..b42d6855332df 100644 --- a/planner/core/physical_plans.go +++ b/planner/core/physical_plans.go @@ -756,6 +756,17 @@ type PhysicalMemTable struct { QueryTimeRange QueryTimeRange } +// MemoryUsage return the memory usage of PhysicalMemTable +func (p *PhysicalMemTable) MemoryUsage() (sum int64) { + if p == nil { + return + } + + sum = p.physicalSchemaProducer.MemoryUsage() + p.DBName.MemoryUsage() + size.SizeOfPointer + size.SizeOfSlice + + int64(cap(p.Columns))*size.SizeOfPointer + size.SizeOfInterface + p.QueryTimeRange.MemoryUsage() + return +} + // PhysicalTableScan represents a table scan plan. type PhysicalTableScan struct { physicalSchemaProducer @@ -1445,6 +1456,21 @@ func (p *PhysicalExchangeReceiver) GetExchangeSender() *PhysicalExchangeSender { return p.children[0].(*PhysicalExchangeSender) } +const emptyMPPTaskSize = int64(unsafe.Sizeof(mppTask{})) + +// MemoryUsage return the memory usage of PhysicalExchangeReceiver +func (p *PhysicalExchangeReceiver) MemoryUsage() (sum int64) { + if p == nil { + return + } + + sum = p.basePhysicalPlan.MemoryUsage() + size.SizeOfSlice*2 + int64(cap(p.Tasks)+cap(p.frags))*size.SizeOfPointer + for _, frag := range p.frags { + sum += frag.MemoryUsage() + } + return +} + // PhysicalExchangeSender dispatches data to upstream tasks. That means push mode processing, type PhysicalExchangeSender struct { basePhysicalPlan @@ -1469,6 +1495,20 @@ func (p *PhysicalExchangeSender) Clone() (PhysicalPlan, error) { return np, nil } +// MemoryUsage return the memory usage of PhysicalExchangeSender +func (p *PhysicalExchangeSender) MemoryUsage() (sum int64) { + if p == nil { + return + } + + sum = p.basePhysicalPlan.MemoryUsage() + size.SizeOfSlice*3 + size.SizeOfInt32 + + int64(cap(p.TargetTasks)+cap(p.HashCols)+cap(p.Tasks))*size.SizeOfPointer + for _, hCol := range p.HashCols { + sum += hCol.MemoryUsage() + } + return +} + // Clone implements PhysicalPlan interface. func (p *PhysicalMergeJoin) Clone() (PhysicalPlan, error) { cloned := new(PhysicalMergeJoin) @@ -1492,6 +1532,29 @@ type PhysicalLock struct { TblID2PhysTblIDCol map[int64]*expression.Column } +// MemoryUsage return the memory usage of PhysicalLock +func (pl *PhysicalLock) MemoryUsage() (sum int64) { + if pl == nil { + return + } + + sum = pl.basePhysicalPlan.MemoryUsage() + size.SizeOfPointer + size.SizeOfMap*2 + if pl.Lock != nil { + sum += int64(unsafe.Sizeof(ast.SelectLockInfo{})) + } + + for _, vals := range pl.TblID2Handle { + sum += size.SizeOfInt64 + size.SizeOfSlice + int64(cap(vals))*size.SizeOfInterface + for _, val := range vals { + sum += val.MemoryUsage() + } + } + for _, val := range pl.TblID2PhysTblIDCol { + sum += size.SizeOfInt64 + size.SizeOfPointer + val.MemoryUsage() + } + return +} + // PhysicalLimit is the physical operator of Limit. type PhysicalLimit struct { physicalSchemaProducer diff --git a/planner/core/planbuilder.go b/planner/core/planbuilder.go index 888326c0d1327..c48d6bd6af8ce 100644 --- a/planner/core/planbuilder.go +++ b/planner/core/planbuilder.go @@ -23,6 +23,7 @@ import ( "strconv" "strings" "time" + "unsafe" "github.com/pingcap/errors" "github.com/pingcap/tidb/bindinfo" @@ -173,6 +174,17 @@ func (tr *QueryTimeRange) Condition() string { return fmt.Sprintf("where time>='%s' and time<='%s'", tr.From.Format(MetricTableTimeFormat), tr.To.Format(MetricTableTimeFormat)) } +const emptyQueryTimeRangeSize = int64(unsafe.Sizeof(QueryTimeRange{})) + +// MemoryUsage return the memory usage of QueryTimeRange +func (tr *QueryTimeRange) MemoryUsage() (sum int64) { + if tr == nil { + return + } + + return emptyQueryTimeRangeSize +} + func tableNames2HintTableInfo(ctx sessionctx.Context, hintName string, hintTables []ast.HintTable, p *hint.BlockHintProcessor, currentOffset int) []hintTableInfo { if len(hintTables) == 0 { return nil diff --git a/planner/core/util.go b/planner/core/util.go index a757a2577b029..5803e2e8233eb 100644 --- a/planner/core/util.go +++ b/planner/core/util.go @@ -221,6 +221,19 @@ func (s *baseSchemaProducer) setSchemaAndNames(schema *expression.Schema, names s.names = names } +// MemoryUsage return the memory usage of baseSchemaProducer +func (s *baseSchemaProducer) MemoryUsage() (sum int64) { + if s == nil { + return + } + + sum = size.SizeOfPointer + size.SizeOfSlice + int64(cap(s.names))*size.SizeOfPointer + s.basePlan.MemoryUsage() + if s.schema != nil { + sum += s.schema.MemoryUsage() + } + return +} + // Schema implements the Plan.Schema interface. func (p *LogicalMaxOneRow) Schema() *expression.Schema { s := p.Children()[0].Schema().Clone() diff --git a/util/size/size.go b/util/size/size.go index 9dc698b84fd22..82cc54c403df2 100644 --- a/util/size/size.go +++ b/util/size/size.go @@ -69,4 +69,10 @@ const ( // SizeOfFunc is the memory each function occupied SizeOfFunc = int64(unsafe.Sizeof(*new(func()))) + + // SizeOfInt64 is the memory each int64 occupied + SizeOfInt64 = int64(unsafe.Sizeof(*new(int64))) + + // SizeOfMap is the memory each map itsel occupied + SizeOfMap = int64(unsafe.Sizeof(*new(map[int]int))) )