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  }