github.com/jxskiss/gopkg/v2@v2.14.9-0.20240514120614-899f3e7952b4/perf/gopool/pool_test.go (about) 1 // Copyright 2021 ByteDance Inc. 2 // Copyright 2023 Shawn Wang <jxskiss@126.com>. 3 // 4 // Licensed under the Apache License, Version 2.0 (the "License"); 5 // you may not use this file except in compliance with the License. 6 // You may obtain a copy of the License at 7 // 8 // http://www.apache.org/licenses/LICENSE-2.0 9 // 10 // Unless required by applicable law or agreed to in writing, software 11 // distributed under the License is distributed on an "AS IS" BASIS, 12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 // See the License for the specific language governing permissions and 14 // limitations under the License. 15 16 package gopool 17 18 import ( 19 "context" 20 "runtime" 21 "sync" 22 "sync/atomic" 23 "testing" 24 "time" 25 ) 26 27 const benchmarkTimes = 10000 28 29 func DoCopyStack(a, b int) int { 30 if b < 100 { 31 return DoCopyStack(0, b+1) 32 } 33 return 0 34 } 35 36 func testFunc() { 37 DoCopyStack(0, 0) 38 } 39 40 func TestPool(t *testing.T) { 41 cfg := NewConfig() 42 cfg.AdhocWorkerLimit = 100 43 p := NewPool(cfg) 44 testWithPool(t, p, 100) 45 } 46 47 func TestPoolWithPermanentWorkers(t *testing.T) { 48 p := NewPool(&Config{ 49 PermanentWorkerNum: 100, 50 AdhocWorkerLimit: 100, 51 }) 52 testWithPool(t, p, 100) 53 } 54 55 func testWithPool(t *testing.T, p *Pool, adhocLimit int32) { 56 var n int32 57 var wg sync.WaitGroup 58 for i := 0; i < 2000; i++ { 59 wg.Add(1) 60 p.Go(func() { 61 defer wg.Done() 62 atomic.AddInt32(&n, 1) 63 if x := p.AdhocWorkerCount(); x > adhocLimit { 64 t.Errorf("adhoc worker count, want <= %d, got %d", adhocLimit, x) 65 } 66 }) 67 } 68 wg.Wait() 69 if n != 2000 { 70 t.Error(n) 71 } 72 time.Sleep(100 * time.Millisecond) 73 if x := p.AdhocWorkerCount(); x != 0 { 74 t.Errorf("adhoc worker count, want 0, got %d", x) 75 } 76 } 77 78 func TestPoolPanic(t *testing.T) { 79 p := NewPool(&Config{AdhocWorkerLimit: 100}) 80 var wg sync.WaitGroup 81 wg.Add(1) 82 p.Go(func() { 83 defer wg.Done() 84 panic("test panic") 85 }) 86 wg.Wait() 87 } 88 89 func BenchmarkDefaultPool(b *testing.B) { 90 p := NewPool(&Config{ 91 AdhocWorkerLimit: runtime.GOMAXPROCS(0), 92 }) 93 benchmarkWithPool(b, p) 94 } 95 96 func BenchmarkPoolWithPermanentWorkers(b *testing.B) { 97 p := NewPool(&Config{ 98 PermanentWorkerNum: runtime.GOMAXPROCS(0), 99 AdhocWorkerLimit: runtime.GOMAXPROCS(0), 100 }) 101 benchmarkWithPool(b, p) 102 } 103 104 func benchmarkWithPool(b *testing.B, p *Pool) { 105 var wg sync.WaitGroup 106 b.ReportAllocs() 107 b.ResetTimer() 108 for i := 0; i < b.N; i++ { 109 wg.Add(benchmarkTimes) 110 for j := 0; j < benchmarkTimes; j++ { 111 p.Go(func() { 112 testFunc() 113 wg.Done() 114 }) 115 } 116 wg.Wait() 117 } 118 } 119 120 func BenchmarkGo(b *testing.B) { 121 var wg sync.WaitGroup 122 b.ReportAllocs() 123 b.ResetTimer() 124 for i := 0; i < b.N; i++ { 125 wg.Add(benchmarkTimes) 126 for j := 0; j < benchmarkTimes; j++ { 127 go func() { 128 testFunc() 129 wg.Done() 130 }() 131 } 132 wg.Wait() 133 } 134 } 135 136 type incInt32Data struct { 137 wg *sync.WaitGroup 138 n *int32 139 } 140 141 func testIncInt32(_ context.Context, arg incInt32Data) { 142 defer arg.wg.Done() 143 atomic.AddInt32(arg.n, 1) 144 } 145 146 func TestTypedPool(t *testing.T) { 147 cfg := NewConfig() 148 cfg.AdhocWorkerLimit = 100 149 p := NewTypedPool(cfg, testIncInt32) 150 testWithTypedPool(t, p) 151 } 152 153 func TestTypedPoolWithPermanentWorkers(t *testing.T) { 154 cfg := &Config{ 155 PermanentWorkerNum: 100, 156 AdhocWorkerLimit: 100, 157 } 158 p := NewTypedPool(cfg, testIncInt32) 159 testWithTypedPool(t, p) 160 } 161 162 func testWithTypedPool(t *testing.T, p *TypedPool[incInt32Data]) { 163 var n int32 164 var wg sync.WaitGroup 165 for i := 0; i < 2000; i++ { 166 wg.Add(1) 167 p.Go(incInt32Data{&wg, &n}) 168 } 169 wg.Wait() 170 if n != 2000 { 171 t.Error(n) 172 } 173 time.Sleep(100 * time.Millisecond) 174 if x := p.AdhocWorkerCount(); x != 0 { 175 t.Errorf("adhoc worker count, want 0, got %d", x) 176 } 177 } 178 179 func TestTypedPoolPanic(t *testing.T) { 180 cfg := &Config{AdhocWorkerLimit: 100} 181 p := NewTypedPool(cfg, func(_ context.Context, arg incInt32Data) { 182 defer arg.wg.Done() 183 panic("test panic") 184 }) 185 186 var n int32 187 var wg sync.WaitGroup 188 wg.Add(1) 189 p.Go(incInt32Data{&wg, &n}) 190 wg.Wait() 191 } 192 193 func BenchmarkTypedPool(b *testing.B) { 194 cfg := &Config{ 195 AdhocWorkerLimit: runtime.GOMAXPROCS(0), 196 } 197 p := NewTypedPool(cfg, func(_ context.Context, wg *sync.WaitGroup) { 198 testFunc() 199 wg.Done() 200 }) 201 benchmarkWithTypedPool(b, p) 202 } 203 204 func BenchmarkTypedPoolWithPermanentWorkers(b *testing.B) { 205 cfg := &Config{ 206 PermanentWorkerNum: runtime.GOMAXPROCS(0), 207 AdhocWorkerLimit: runtime.GOMAXPROCS(0), 208 } 209 p := NewTypedPool(cfg, func(_ context.Context, wg *sync.WaitGroup) { 210 testFunc() 211 wg.Done() 212 }) 213 benchmarkWithTypedPool(b, p) 214 } 215 216 func benchmarkWithTypedPool(b *testing.B, p *TypedPool[*sync.WaitGroup]) { 217 var wg sync.WaitGroup 218 b.ReportAllocs() 219 b.ResetTimer() 220 for i := 0; i < b.N; i++ { 221 wg.Add(benchmarkTimes) 222 for j := 0; j < benchmarkTimes; j++ { 223 p.Go(&wg) 224 } 225 wg.Wait() 226 } 227 } 228 229 func TestSetAdhocWorkerLimit(t *testing.T) { 230 pool := NewPool(&Config{AdhocWorkerLimit: 100}) 231 wg := &sync.WaitGroup{} 232 233 wg.Add(100) 234 for i := 0; i < 100; i++ { 235 go func() { 236 pool.SetAdhocWorkerLimit(80) 237 wg.Done() 238 }() 239 } 240 wg.Wait() 241 if x := pool.AdhocWorkerLimit(); x != 80 { 242 t.Errorf("adhoc worker limit not match, want 80, got %d", x) 243 } 244 if x := pool.AdhocWorkerCount(); x != 0 { 245 t.Errorf("adhoc worker count not match, want 0, got %d", x) 246 } 247 248 wg.Add(100) 249 for i := 0; i < 100; i++ { 250 go func() { 251 pool.SetAdhocWorkerLimit(100) 252 wg.Done() 253 }() 254 } 255 wg.Wait() 256 if x := pool.AdhocWorkerLimit(); x != 100 { 257 t.Errorf("adhoc worker limit not match, want 100, got %d", x) 258 } 259 if x := pool.AdhocWorkerCount(); x != 0 { 260 t.Errorf("adhoc worker count not match, want 0, got %d", x) 261 } 262 263 wg.Add(100) 264 for i := 0; i < 100; i++ { 265 limit := 80 + 20*(i%2) 266 go func() { 267 pool.SetAdhocWorkerLimit(limit) 268 wg.Done() 269 }() 270 } 271 wg.Wait() 272 if x := pool.AdhocWorkerLimit(); x != 100 && x != 80 { 273 t.Errorf("adhoc worker limit not match, got %d", x) 274 } 275 if x := pool.AdhocWorkerCount(); x != 0 { 276 t.Errorf("adhoc worker count not match, want 0, got %d", x) 277 } 278 }