github.com/NVIDIA/aistore@v1.3.23-0.20240517131212-7df6609be51d/memsys/a_test.go (about)

     1  // Package memsys provides memory management and Slab allocation
     2  // with io.Reader and io.Writer interfaces on top of a scatter-gather lists
     3  // (of reusable buffers)
     4  /*
     5   * Copyright (c) 2018-2024, NVIDIA CORPORATION. All rights reserved.
     6   */
     7  package memsys_test
     8  
     9  // How to run:
    10  //
    11  // 1) run each of the tests for 2 minutes in debug mode:
    12  // go test -v -duration 2m -tags=debug
    13  //
    14  // 2) run each test for 10 minutes with the permission to use up to 90% of total RAM
    15  // AIS_MINMEM_PCT_TOTAL=10 go test -v -run=No -duration 10m -timeout=1h
    16  
    17  import (
    18  	"flag"
    19  	"fmt"
    20  	"os"
    21  	"sync"
    22  	"testing"
    23  	"time"
    24  
    25  	"github.com/NVIDIA/aistore/cmn/cos"
    26  	"github.com/NVIDIA/aistore/memsys"
    27  	"github.com/NVIDIA/aistore/tools/tlog"
    28  )
    29  
    30  var (
    31  	duration time.Duration // test duration
    32  	verbose  bool
    33  )
    34  
    35  func TestMain(t *testing.M) {
    36  	var (
    37  		d   string
    38  		err error
    39  	)
    40  	flag.StringVar(&d, "duration", "30s", "test duration")
    41  	flag.BoolVar(&verbose, "verbose", false, "verbose")
    42  	flag.Parse()
    43  
    44  	if duration, err = time.ParseDuration(d); err != nil {
    45  		cos.Exitf("Invalid duration %q", d)
    46  	}
    47  
    48  	os.Exit(t.Run())
    49  }
    50  
    51  func Test_Sleep(*testing.T) {
    52  	if testing.Short() {
    53  		duration = 4 * time.Second
    54  	}
    55  
    56  	mem := &memsys.MMSA{Name: "amem", TimeIval: time.Second * 20, MinFree: cos.GiB}
    57  	mem.Init(0)
    58  
    59  	wg := &sync.WaitGroup{}
    60  	random := cos.NowRand()
    61  	for i := range 100 {
    62  		ttl := time.Duration(random.Int63n(int64(time.Millisecond*100))) + time.Millisecond
    63  		var siz, tot int64
    64  		if i%2 == 0 {
    65  			siz = random.Int63n(cos.MiB*10) + cos.KiB
    66  		} else {
    67  			siz = random.Int63n(cos.KiB*100) + cos.KiB
    68  		}
    69  		tot = random.Int63n(cos.DivCeil(cos.MiB*50, siz))*siz + cos.KiB
    70  		wg.Add(1)
    71  		go memstress(mem, i, ttl, siz, tot, wg)
    72  	}
    73  	c := make(chan struct{}, 1)
    74  	go printMaxRingLen(mem, c)
    75  	for range 7 {
    76  		time.Sleep(duration / 8)
    77  		mem.FreeSpec(memsys.FreeSpec{IdleDuration: 1, MinSize: cos.MiB})
    78  	}
    79  	wg.Wait()
    80  	close(c)
    81  	mem.Terminate(false)
    82  }
    83  
    84  func Test_NoSleep(*testing.T) {
    85  	if testing.Short() {
    86  		duration = 4 * time.Second
    87  	}
    88  
    89  	mem := &memsys.MMSA{Name: "bmem", TimeIval: time.Second * 20, MinPctTotal: 5}
    90  	mem.Init(0)
    91  	go printStats(mem)
    92  
    93  	wg := &sync.WaitGroup{}
    94  	random := cos.NowRand()
    95  	for i := range 500 {
    96  		siz := random.Int63n(cos.MiB) + cos.KiB
    97  		tot := random.Int63n(cos.DivCeil(cos.KiB*10, siz))*siz + cos.KiB
    98  		wg.Add(1)
    99  		go memstress(mem, i, time.Millisecond, siz, tot, wg)
   100  	}
   101  	c := make(chan struct{}, 1)
   102  	go printMaxRingLen(mem, c)
   103  	for range 7 {
   104  		time.Sleep(duration / 8)
   105  		mem.FreeSpec(memsys.FreeSpec{Totally: true, ToOS: true, MinSize: cos.MiB * 10})
   106  	}
   107  	wg.Wait()
   108  	close(c)
   109  	mem.Terminate(false)
   110  }
   111  
   112  func printMaxRingLen(mem *memsys.MMSA, c chan struct{}) {
   113  	for range 100 {
   114  		select {
   115  		case <-c:
   116  			return
   117  		case <-time.After(5 * time.Second):
   118  			if p := mem.Pressure(); p > memsys.PressureLow {
   119  				tlog.Logf("mem-pressure %d\n", p)
   120  			}
   121  		}
   122  	}
   123  }
   124  
   125  func memstress(mem *memsys.MMSA, id int, ttl time.Duration, siz, tot int64, wg *sync.WaitGroup) {
   126  	defer wg.Done()
   127  	sgls := make([]*memsys.SGL, 0, 128)
   128  	x := cos.ToSizeIEC(siz, 1) + "/" + cos.ToSizeIEC(tot, 1)
   129  	if id%100 == 0 && verbose {
   130  		if ttl > time.Millisecond {
   131  			tlog.Logf("%4d: %-19s ttl %v\n", id, x, ttl)
   132  		} else {
   133  			tlog.Logf("%4d: %-19s\n", id, x)
   134  		}
   135  	}
   136  	started := time.Now()
   137  	for {
   138  		t := tot
   139  		for t > 0 {
   140  			sgls = append(sgls, mem.NewSGL(siz))
   141  			t -= siz
   142  		}
   143  		time.Sleep(ttl)
   144  		for i, sgl := range sgls {
   145  			sgl.Free()
   146  			sgls[i] = nil
   147  		}
   148  		sgls = sgls[:0]
   149  		if time.Since(started) > duration {
   150  			break
   151  		}
   152  	}
   153  	if id%100 == 0 && verbose {
   154  		tlog.Logf("%4d: done\n", id)
   155  	}
   156  }
   157  
   158  func printStats(mem *memsys.MMSA) {
   159  	for {
   160  		time.Sleep(mem.TimeIval)
   161  		stats := mem.GetStats()
   162  		for i := range memsys.NumPageSlabs {
   163  			slab, err := mem.GetSlab(int64(i+1) * memsys.PageSize)
   164  			cos.AssertNoErr(err)
   165  			x := ""
   166  			idle := stats.Idle[i]
   167  			if idle > 0 {
   168  				x = fmt.Sprintf(", idle=%v", idle)
   169  			}
   170  			fmt.Printf("%s: hits %d%s\n", slab.Tag(), stats.Hits[i], x)
   171  		}
   172  	}
   173  }