github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/util/quotapool/bench_test.go (about) 1 // Copyright 2019 The Cockroach Authors. 2 // 3 // Use of this software is governed by the Business Source License 4 // included in the file licenses/BSL.txt. 5 // 6 // As of the Change Date specified in that file, in accordance with 7 // the Business Source License, use of this software will be governed 8 // by the Apache License, Version 2.0, included in the file 9 // licenses/APL.txt. 10 11 package quotapool_test 12 13 import ( 14 "context" 15 "fmt" 16 "runtime" 17 "testing" 18 19 "github.com/cockroachdb/cockroach/pkg/util/quotapool" 20 "golang.org/x/sync/errgroup" 21 ) 22 23 // BenchmarkIntQuotaPool benchmarks the common case where we have sufficient 24 // quota available in the pool and we repeatedly acquire and release quota. 25 func BenchmarkIntPool(b *testing.B) { 26 qp := quotapool.NewIntPool("test", 1) 27 ctx := context.Background() 28 for n := 0; n < b.N; n++ { 29 alloc, err := qp.Acquire(ctx, 1) 30 if err != nil { 31 b.Fatal(err) 32 } 33 alloc.Release() 34 } 35 qp.Close("") 36 } 37 38 func BenchmarkChannelSemaphore(b *testing.B) { 39 sem := make(chan struct{}, 1) 40 ctx := context.Background() 41 for n := 0; n < b.N; n++ { 42 select { 43 case <-ctx.Done(): 44 case sem <- struct{}{}: 45 } 46 select { 47 case <-ctx.Done(): 48 case <-sem: 49 } 50 } 51 close(sem) 52 } 53 54 // BenchmarkConcurrentIntPool benchmarks concurrent workers in a variety 55 // of ratios between adequate and inadequate quota to concurrently serve all 56 // workers with the IntPool. 57 func BenchmarkConcurrentIntPool(b *testing.B) { 58 for _, s := range concurrentBenchSpecs { 59 b.Run(s.String(), s.benchmarkIntPool) 60 } 61 } 62 63 // BenchmarkConcurrentChannelSem benchmarks concurrent workers in a variety 64 // of ratios between adequate and inadequate quota to concurrently serve all 65 // workers with a channel-based semaphore to compare the performance against 66 // the IntPool. 67 func BenchmarkConcurrentChannelSemaphore(b *testing.B) { 68 for _, s := range concurrentBenchSpecs { 69 b.Run(s.String(), s.benchmarkChannelSem) 70 } 71 } 72 73 // BenchmarkIntQuotaPoolFunc benchmarks the common case where we have sufficient 74 // quota available in the pool and we repeatedly acquire and release quota. 75 func BenchmarkIntPoolFunc(b *testing.B) { 76 qp := quotapool.NewIntPool("test", 1, logSlowAcquisition) 77 ctx := context.Background() 78 toAcquire := intRequest(1) 79 for n := 0; n < b.N; n++ { 80 alloc, err := qp.AcquireFunc(ctx, toAcquire.acquire) 81 if err != nil { 82 b.Fatal(err) 83 } else if acquired := alloc.Acquired(); acquired != 1 { 84 b.Fatalf("expected to acquire %d, got %d", 1, acquired) 85 } 86 alloc.Release() 87 } 88 qp.Close("") 89 } 90 91 type concurrentBenchSpec struct { 92 workers int 93 quota uint64 94 } 95 96 func (s concurrentBenchSpec) benchmarkChannelSem(b *testing.B) { 97 sem := make(chan struct{}, s.quota) 98 g, ctx := errgroup.WithContext(context.Background()) 99 runWorker := func(workerNum int) { 100 g.Go(func() error { 101 for i := workerNum; i < b.N; i += s.workers { 102 select { 103 case <-ctx.Done(): 104 case sem <- struct{}{}: 105 } 106 runtime.Gosched() 107 select { 108 case <-ctx.Done(): 109 case <-sem: 110 } 111 } 112 return nil 113 }) 114 } 115 for i := 0; i < s.workers; i++ { 116 runWorker(i) 117 } 118 if err := g.Wait(); err != nil { 119 b.Fatal(err) 120 } 121 close(sem) 122 } 123 124 func (s concurrentBenchSpec) benchmarkIntPool(b *testing.B) { 125 qp := quotapool.NewIntPool("test", s.quota, logSlowAcquisition) 126 g, ctx := errgroup.WithContext(context.Background()) 127 runWorker := func(workerNum int) { 128 g.Go(func() error { 129 for i := workerNum; i < b.N; i += s.workers { 130 alloc, err := qp.Acquire(ctx, 1) 131 if err != nil { 132 b.Fatal(err) 133 } 134 runtime.Gosched() 135 alloc.Release() 136 } 137 return nil 138 }) 139 } 140 for i := 0; i < s.workers; i++ { 141 runWorker(i) 142 } 143 if err := g.Wait(); err != nil { 144 b.Fatal(err) 145 } 146 qp.Close("") 147 } 148 149 func (s concurrentBenchSpec) String() string { 150 return fmt.Sprintf("workers=%d,quota=%d", s.workers, s.quota) 151 } 152 153 var concurrentBenchSpecs = []concurrentBenchSpec{ 154 {1, 1}, 155 {2, 2}, 156 {8, 4}, 157 {128, 4}, 158 {512, 128}, 159 {512, 513}, 160 {512, 511}, 161 {1024, 4}, 162 {1024, 4096}, 163 } 164 165 // intRequest is a wrapper to create a IntRequestFunc from an int64. 166 type intRequest uint64 167 168 func (ir intRequest) acquire(_ context.Context, pi quotapool.PoolInfo) (took uint64, err error) { 169 if uint64(ir) < pi.Available { 170 return 0, quotapool.ErrNotEnoughQuota 171 } 172 return uint64(ir), nil 173 }