github.com/NVIDIA/aistore@v1.3.23-0.20240517131212-7df6609be51d/ext/dsort/adjuster_internal_test.go (about) 1 // Package dsort provides APIs for distributed archive file shuffling. 2 /* 3 * Copyright (c) 2018-2024, NVIDIA CORPORATION. All rights reserved. 4 */ 5 package dsort 6 7 import ( 8 "os" 9 "sync" 10 "time" 11 12 "github.com/NVIDIA/aistore/cmn" 13 "github.com/NVIDIA/aistore/cmn/atomic" 14 "github.com/NVIDIA/aistore/cmn/cos" 15 "github.com/NVIDIA/aistore/core/mock" 16 "github.com/NVIDIA/aistore/fs" 17 . "github.com/onsi/ginkgo/v2" 18 . "github.com/onsi/gomega" 19 ) 20 21 const ( 22 testingConfigDir = "/tmp/ais_tests" 23 ) 24 25 func calcSemaLimit(acquire, release func()) int { 26 var i atomic.Int32 27 wg := &sync.WaitGroup{} 28 ch := make(chan int32, 200) 29 30 for range 200 { 31 acquire() 32 wg.Add(1) 33 go func() { 34 ch <- i.Inc() 35 time.Sleep(50 * time.Microsecond) 36 i.Dec() 37 release() 38 wg.Done() 39 }() 40 } 41 42 wg.Wait() 43 close(ch) 44 45 res := int32(0) 46 for c := range ch { 47 res = max(res, c) 48 } 49 50 return int(res) 51 } 52 53 var _ = Describe("newConcAdjuster", func() { 54 mios := mock.NewIOS() 55 56 BeforeEach(func() { 57 err := cos.CreateDir(testingConfigDir) 58 Expect(err).ShouldNot(HaveOccurred()) 59 60 fs.TestNew(mios) 61 _, _ = fs.Add(testingConfigDir, "daeID") 62 63 config := cmn.GCO.BeginUpdate() 64 config.Disk.IostatTimeShort = cos.Duration(10 * time.Millisecond) 65 config.Disk.DiskUtilLowWM = 70 66 config.Disk.DiskUtilHighWM = 80 67 config.Disk.DiskUtilMaxWM = 95 68 cmn.GCO.CommitUpdate(config) 69 }) 70 71 AfterEach(func() { 72 err := os.RemoveAll(testingConfigDir) 73 Expect(err).ShouldNot(HaveOccurred()) 74 }) 75 76 It("should not have more goroutines than specified", func() { 77 var ( 78 adjuster = newConcAdjuster(0, 3) 79 expectedLimit = defaultConcFuncLimit * 3 80 ) 81 82 limit := calcSemaLimit(adjuster.acquireGoroutineSema, adjuster.releaseGoroutineSema) 83 Expect(limit).To(Equal(expectedLimit)) 84 }) 85 86 It("should not have more goroutines than specified max limit", func() { 87 var ( 88 adjuster = newConcAdjuster(2, 3) 89 expectedLimit = 2 * 3 90 ) 91 92 limit := calcSemaLimit(adjuster.acquireGoroutineSema, adjuster.releaseGoroutineSema) 93 Expect(limit).To(Equal(expectedLimit)) 94 }) 95 96 It("should converge to perfect limit", func() { 97 cfg := cmn.GCO.Get() 98 99 var ( 100 perfectLimit = 13 101 perfectUtil = int(cfg.Disk.DiskUtilMaxWM+cfg.Disk.DiskUtilHighWM) / 2 102 ) 103 availablePaths := fs.GetAvail() 104 mi := availablePaths[testingConfigDir] 105 106 adjuster := newConcAdjuster(0, 1) 107 108 adjuster.start() 109 defer adjuster.stop() 110 111 for { 112 curLimit := calcSemaLimit(func() { 113 adjuster.acquireSema(mi) 114 }, func() { 115 adjuster.releaseSema(mi) 116 }) 117 118 // If we get enough close we can just break 119 if cos.Abs(curLimit-perfectLimit) <= 1 { 120 break 121 } 122 123 curUtil := perfectUtil * curLimit / perfectLimit 124 mios.Utils.Set(testingConfigDir, int64(curUtil)) 125 126 time.Sleep(time.Millisecond) // make sure that concurrency adjuster processed all information 127 } 128 }) 129 })