github.com/matrixorigin/matrixone@v1.2.0/pkg/vm/engine/tae/containers/pool.go (about) 1 // Copyright 2021 Matrix Origin 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package containers 16 17 import ( 18 "fmt" 19 "sync/atomic" 20 _ "unsafe" 21 22 "github.com/matrixorigin/matrixone/pkg/common/mpool" 23 "github.com/matrixorigin/matrixone/pkg/container/types" 24 "github.com/matrixorigin/matrixone/pkg/container/vector" 25 "github.com/matrixorigin/matrixone/pkg/util/metric/stats" 26 ) 27 28 const ( 29 // fix sized / (varlen + fix sized) 30 defaultFixedSizeRatio = 0.6 31 32 // default max alloaction size for a vector 33 defaultAllocLimit = 1024 * 1024 * 2 // 2MB 34 ) 35 36 var _vectorPoolAlloactor *mpool.MPool 37 38 func init() { 39 var err error 40 if _vectorPoolAlloactor, err = mpool.NewMPool( 41 "taeVectorPool", 0, mpool.NoFixed, 42 ); err != nil { 43 panic(err) 44 } 45 } 46 47 func GetDefaultVectorPoolALLocator() *mpool.MPool { 48 return _vectorPoolAlloactor 49 } 50 51 type VectorPoolOption func(*VectorPool) 52 53 func WithAllocationLimit(maxv int) VectorPoolOption { 54 return func(p *VectorPool) { 55 p.maxAlloc = maxv 56 } 57 } 58 59 func WithFixedSizeRatio(ratio float64) VectorPoolOption { 60 return func(p *VectorPool) { 61 p.ratio = ratio 62 } 63 } 64 65 func WithMPool(mp *mpool.MPool) VectorPoolOption { 66 return func(p *VectorPool) { 67 p.mp = mp 68 } 69 } 70 71 type vectorPoolElement struct { 72 pool *VectorPool 73 mp *mpool.MPool 74 vec *vector.Vector 75 inUse atomic.Bool 76 } 77 78 func (e *vectorPoolElement) tryReuse(t *types.Type) bool { 79 if e.inUse.CompareAndSwap(false, true) { 80 e.vec.ResetWithNewType(t) 81 return true 82 } 83 return false 84 } 85 86 func (e *vectorPoolElement) put() { 87 if e.vec.Allocated() > e.pool.maxAlloc { 88 newVec := vector.NewVec(*e.vec.GetType()) 89 e.vec.Free(e.mp) 90 e.vec = newVec 91 e.pool.resetStats.Add(1) 92 } 93 if !e.inUse.CompareAndSwap(true, false) { 94 panic("vectorPoolElement.put: vector is not in use") 95 } 96 } 97 98 func (e *vectorPoolElement) isIdle() bool { 99 return !e.inUse.Load() 100 } 101 102 type VectorPool struct { 103 name string 104 maxAlloc int 105 ratio float64 106 fixSizedPool []*vectorPoolElement 107 varlenPool []*vectorPoolElement 108 fixHitStats stats.Counter 109 varlenHitStats stats.Counter 110 resetStats stats.Counter 111 totalStats stats.Counter 112 mp *mpool.MPool 113 } 114 115 func NewVectorPool(name string, cnt int, opts ...VectorPoolOption) *VectorPool { 116 p := &VectorPool{ 117 name: name, 118 ratio: -1, 119 } 120 for _, opt := range opts { 121 opt(p) 122 } 123 if p.mp == nil { 124 p.mp = _vectorPoolAlloactor 125 } 126 if p.maxAlloc <= 0 { 127 p.maxAlloc = defaultAllocLimit 128 } 129 if p.ratio < 0 { 130 p.ratio = defaultFixedSizeRatio 131 } 132 133 cnt1 := int(float64(cnt) * p.ratio) 134 cnt2 := cnt - cnt1 135 p.fixSizedPool = make([]*vectorPoolElement, 0, cnt1) 136 p.varlenPool = make([]*vectorPoolElement, 0, cnt2) 137 138 for i := 0; i < cnt1; i++ { 139 t := types.T_int64.ToType() 140 p.fixSizedPool = append(p.fixSizedPool, newVectorElement(p, &t, p.mp)) 141 } 142 for i := 0; i < cnt2; i++ { 143 t := types.T_varchar.ToType() 144 p.varlenPool = append(p.varlenPool, newVectorElement(p, &t, p.mp)) 145 } 146 return p 147 } 148 149 func (p *VectorPool) String() string { 150 fixedUsedCnt, _ := p.FixedSizeUsed(false) 151 varlenUsedCnt, _ := p.VarlenUsed(false) 152 usedCnt := fixedUsedCnt + varlenUsedCnt 153 fixHit := p.fixHitStats.Load() 154 varlenHit := p.varlenHitStats.Load() 155 hit := fixHit + varlenHit 156 str := fmt.Sprintf( 157 "VectorPool[%s][%d/%d]: FixSizedVec[%d/%d] VarlenVec[%d/%d], Reset/Hit/totalStats:[%d/(%d,%d)%d/%d]", 158 p.name, /* name */ 159 usedCnt, /* totalStats used vector cnt */ 160 len(p.fixSizedPool)+len(p.varlenPool), /* totalStats vector cnt */ 161 fixedUsedCnt, /* used fixed sized vector cnt */ 162 len(p.fixSizedPool), /* totalStats fixed sized vector cnt */ 163 varlenUsedCnt, /* used varlen vector cnt */ 164 len(p.varlenPool), /* totalStats varlen vector cnt */ 165 p.resetStats.Load(), /* reset cnt */ 166 fixHit, /* fixed sized vector hit cnt */ 167 varlenHit, /* varlen vector hit cnt */ 168 hit, /* hit cnt */ 169 p.totalStats.Load(), /* totalStats cnt */ 170 ) 171 return str 172 } 173 174 func (p *VectorPool) GetVector(t *types.Type) *vectorWrapper { 175 p.totalStats.Add(1) 176 if t.IsFixedLen() { 177 if len(p.fixSizedPool) > 0 { 178 for i := 0; i < 4; i++ { 179 idx := fastrand() % uint32(len(p.fixSizedPool)) 180 element := p.fixSizedPool[idx] 181 if element.tryReuse(t) { 182 p.fixHitStats.Add(1) 183 return &vectorWrapper{ 184 wrapped: element.vec, 185 mpool: element.mp, 186 element: element, 187 } 188 } 189 } 190 } 191 } else { 192 if len(p.varlenPool) > 0 { 193 for i := 0; i < 4; i++ { 194 idx := fastrand() % uint32(len(p.varlenPool)) 195 element := p.varlenPool[idx] 196 if element.tryReuse(t) { 197 p.varlenHitStats.Add(1) 198 return &vectorWrapper{ 199 wrapped: element.vec, 200 mpool: element.mp, 201 element: element, 202 } 203 } 204 } 205 } 206 } 207 208 return NewVector(*t, Options{Allocator: p.mp}) 209 } 210 211 func (p *VectorPool) GetMPool() *mpool.MPool { 212 return p.mp 213 } 214 215 func (p *VectorPool) Allocated() int { 216 size := 0 217 size += p.FixedSizeAllocated() 218 size += p.VarlenaSizeAllocated() 219 return size 220 } 221 222 func (p *VectorPool) FixedSizeAllocated() int { 223 size := 0 224 for _, element := range p.fixSizedPool { 225 size += element.vec.Allocated() 226 } 227 return size 228 } 229 230 func (p *VectorPool) VarlenaSizeAllocated() int { 231 size := 0 232 for _, element := range p.varlenPool { 233 size += element.vec.Allocated() 234 } 235 return size 236 } 237 238 func (p *VectorPool) FixedSizeUsed(isUnsafe bool) (int, int) { 239 size := 0 240 cnt := 0 241 for _, element := range p.fixSizedPool { 242 if element.isIdle() { 243 continue 244 } 245 if isUnsafe { 246 size += element.vec.Allocated() 247 } 248 cnt++ 249 } 250 return cnt, size 251 } 252 253 func (p *VectorPool) VarlenUsed(isUnsafe bool) (int, int) { 254 size := 0 255 cnt := 0 256 for _, element := range p.varlenPool { 257 if element.isIdle() { 258 continue 259 } 260 if isUnsafe { 261 size += element.vec.Allocated() 262 } 263 cnt++ 264 } 265 return cnt, size 266 } 267 268 func (p *VectorPool) Used(isUnsafe bool) (int, int) { 269 cnt, size := p.FixedSizeUsed(isUnsafe) 270 cnt2, size2 := p.VarlenUsed(isUnsafe) 271 cnt += cnt2 272 size += size2 273 return cnt, size 274 } 275 276 // Only for test 277 // It is not safe to call Destory 278 func (p *VectorPool) Destory() { 279 fixSizedPool := p.fixSizedPool 280 varlenPool := p.varlenPool 281 mp := p.mp 282 p.mp = nil 283 p.fixSizedPool = nil 284 p.varlenPool = nil 285 286 for _, elem := range fixSizedPool { 287 elem.vec.Free(mp) 288 } 289 for _, elem := range varlenPool { 290 elem.vec.Free(mp) 291 } 292 } 293 294 func newVectorElement(pool *VectorPool, t *types.Type, mp *mpool.MPool) *vectorPoolElement { 295 vec := vector.NewVec(*t) 296 element := &vectorPoolElement{ 297 pool: pool, 298 mp: mp, 299 vec: vec, 300 } 301 return element 302 } 303 304 //go:linkname fastrand runtime.fastrand 305 func fastrand() uint32