github.com/NVIDIA/aistore@v1.3.23-0.20240517131212-7df6609be51d/memsys/init.go (about) 1 // Package memsys provides memory management and slab/SGL allocation with io.Reader and io.Writer interfaces 2 // on top of scatter-gather lists of reusable buffers. 3 /* 4 * Copyright (c) 2018-2024, NVIDIA CORPORATION. All rights reserved. 5 */ 6 package memsys 7 8 import ( 9 "fmt" 10 "sync" 11 12 "github.com/NVIDIA/aistore/cmn" 13 "github.com/NVIDIA/aistore/cmn/cos" 14 "github.com/NVIDIA/aistore/cmn/debug" 15 "github.com/NVIDIA/aistore/cmn/nlog" 16 "github.com/NVIDIA/aistore/hk" 17 ) 18 19 const ( 20 minMemFree = cos.GiB + cos.GiB>>1 // default minimum memory (see description above) 21 minMemFreeTests = cos.MiB * 128 // minimum free to run tests 22 maxMemUsedTests = cos.GiB * 10 // maximum tests allowed to allocate 23 ) 24 25 var ( 26 gmm *MMSA // page-based system allocator 27 smm *MMSA // slab allocator for small sizes in the range 1 - 4K 28 gmmOnce, smmOnce sync.Once // ensures singleton-ness 29 ) 30 31 func Init(gmmName, smmName string, config *cmn.Config) { 32 debug.Assert(gmm == nil && smm == nil) 33 34 // page mmsa config (see also "AIS_MINMEM_FREE" and related environment) 35 defBufSize := int64(DefaultBufSize) 36 if config.Memsys.DefaultBufSize != 0 { 37 defBufSize = int64(config.Memsys.DefaultBufSize) 38 } 39 gmm = &MMSA{Name: gmmName + ".gmm", defBufSize: defBufSize, slabIncStep: PageSlabIncStep} 40 gmm.MinFree = uint64(config.Memsys.MinFree) 41 gmm.MinPctTotal = config.Memsys.MinPctTotal 42 gmm.MinPctFree = config.Memsys.MinPctFree 43 44 // hk config 45 if config.Memsys.SizeToGC != 0 { 46 sizeToGC = int64(config.Memsys.SizeToGC) 47 } 48 if config.Memsys.HousekeepTime != 0 { 49 memCheckAbove = config.Memsys.HousekeepTime.D() 50 } 51 52 gmm.Init(0) 53 nlog.InfoDepth(1, "memory:", gmm.Str(&gmm.mem)) 54 55 // byte mmsa: 56 smm = &MMSA{Name: smmName + ".smm", defBufSize: DefaultSmallBufSize, slabIncStep: SmallSlabIncStep} 57 smm.Init(0) 58 smm.sibling = gmm 59 gmm.sibling = smm 60 } 61 62 func NewMMSA(name string, silent bool) (mem *MMSA, err error) { 63 mem = &MMSA{defBufSize: DefaultBufSize, slabIncStep: PageSlabIncStep, MinFree: minMemFreeTests} 64 if gmm == nil { 65 // (alt) gmm via alternative init path - prevent once.do below 66 mem.Name = name + ".gmm" 67 gmm = mem 68 } else { 69 mem.Name = name + ".pmm" // additional 70 } 71 err = mem.Init(0) 72 if !silent { 73 cos.Infof("%s", mem.Str(&mem.mem)) 74 } 75 return 76 } 77 78 // system page-based memory-manager-slab-allocator (MMSA) 79 func PageMM() *MMSA { 80 gmmOnce.Do(func() { 81 if gmm == nil { 82 // tests calling PageMM without prior explicit NewMMSA() 83 // (tests only) 84 gmm = &MMSA{ 85 Name: "test.pmm", 86 defBufSize: DefaultBufSize, 87 slabIncStep: PageSlabIncStep, 88 MinFree: minMemFreeTests, 89 } 90 gmm.Init(maxMemUsedTests) 91 if smm != nil { 92 smm.sibling = gmm 93 gmm.sibling = smm 94 } 95 } 96 debug.Assert(gmm.rings != nil) 97 }) 98 return gmm 99 } 100 101 // system small-size allocator (range 1 - 4K) 102 func ByteMM() *MMSA { 103 smmOnce.Do(func() { 104 if smm == nil { 105 // tests only 106 smm = &MMSA{ 107 Name: "test.smm", 108 defBufSize: DefaultSmallBufSize, 109 slabIncStep: SmallSlabIncStep, 110 MinFree: minMemFreeTests, 111 } 112 smm.Init(maxMemUsedTests) 113 if gmm != nil { 114 gmm.sibling = smm 115 smm.sibling = gmm 116 } 117 } 118 debug.Assert(smm.rings != nil) 119 }) 120 return smm 121 } 122 123 // byte vs page rings: (NumSmallSlabs x 128) vs (NumPageSlabs x 4K), respectively 124 func (r *MMSA) isPage() bool { return r.slabIncStep == PageSlabIncStep } 125 126 func (r *MMSA) RegWithHK() { 127 d := r.TimeIval 128 _ = r.mem.Get() 129 free := memFree(&r.mem) 130 if free < r.lowWM || free < minMemFree { 131 d >>= 1 132 } 133 hk.Reg(r.Name+hk.NameSuffix, r.hkcb, d) 134 } 135 136 // initialize new MMSA instance 137 func (r *MMSA) Init(maxUse int64) (err error) { 138 // 1. environment overrides defaults and MMSA{...} hard-codings 139 if err = r.env(); err != nil { 140 cos.Errorf("%v", err) 141 } 142 143 // 2. compute min-free (must remain free at all times) and low watermark 144 err = r.mem.Get() 145 if err != nil { 146 cos.Errorf("%v", err) 147 } 148 free := memFree(&r.mem) 149 if r.MinPctTotal > 0 { 150 x := r.mem.Total * uint64(r.MinPctTotal) / 100 151 if r.MinFree == 0 { 152 r.MinFree = x 153 } else { 154 r.MinFree = min(r.MinFree, x) 155 } 156 } 157 if r.MinPctFree > 0 { 158 x := free * uint64(r.MinPctFree) / 100 159 if r.MinFree == 0 { 160 r.MinFree = x 161 } else { 162 r.MinFree = min(r.MinFree, x) 163 } 164 } 165 if maxUse > 0 { 166 r.MinFree = max(r.MinFree, free-uint64(maxUse)) 167 } 168 if r.MinFree == 0 { 169 r.MinFree = minMemFree 170 } 171 r.lowWM = (r.MinFree+free)>>1 - (r.MinFree+free)>>4 // a quarter of 172 r.lowWM = max(r.lowWM, r.MinFree+minMemFreeTests) 173 174 // 3. validate min-free & low-wm 175 if free < min(r.MinFree*2, r.MinFree+minMemFree) { 176 err = fmt.Errorf("memsys: insufficient free memory %s (see %s for guidance)", r.Str(&r.mem), readme) 177 cos.Errorf("%v", err) 178 r.lowWM = min(r.lowWM, r.MinFree+minMemFreeTests) 179 r.info = "" 180 } 181 182 // 4. timer 183 if r.TimeIval == 0 { 184 r.TimeIval = memCheckAbove 185 } 186 187 // 5. final construction steps 188 r.swap.size.Store(r.mem.SwapUsed) 189 r.optDepth.Store(optDepth) 190 r.toGC.Store(0) 191 192 if r.defBufSize == 0 { 193 // assume page 194 debug.Assert(r.slabIncStep == 0 || r.slabIncStep == PageSlabIncStep) 195 r.defBufSize = DefaultBufSize 196 r.slabIncStep = PageSlabIncStep 197 } 198 r.maxSlabSize, r.numSlabs = MaxPageSlabSize, NumPageSlabs 199 if !r.isPage() { 200 r.maxSlabSize, r.numSlabs = MaxSmallSlabSize, NumSmallSlabs 201 } 202 r.slabStats = &slabStats{} 203 r.statsSnapshot = &Stats{} 204 r.rings = make([]*Slab, r.numSlabs) 205 r.sorted = make([]*Slab, r.numSlabs) 206 for i := range r.numSlabs { 207 bufSize := r.slabIncStep * int64(i+1) 208 slab := &Slab{ 209 m: r, 210 tag: r.Name + "." + cos.ToSizeIEC(bufSize, 0), 211 bufSize: bufSize, 212 get: make([][]byte, 0, optDepth), 213 put: make([][]byte, 0, optDepth), 214 } 215 slab.pMinDepth = &r.optDepth 216 r.rings[i] = slab 217 r.sorted[i] = slab 218 } 219 return 220 } 221 222 // terminate this MMSA instance and, possibly, GC as well 223 func (r *MMSA) Terminate(unregHK bool) { 224 var freed int64 225 if unregHK { 226 hk.Unreg(r.Name + hk.NameSuffix) 227 } 228 for _, s := range r.rings { 229 freed += s.cleanup() 230 } 231 r.toGC.Add(freed) 232 r.freeMemToOS(sizeToGC, true /*force*/) 233 debug.Infof("%s terminated", r) 234 }