github.com/dubbogo/gost@v1.14.0/sync/task_pool_test.go (about) 1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18 package gxsync 19 20 import ( 21 "math/rand" 22 "runtime" 23 "sync" 24 "sync/atomic" 25 "testing" 26 "time" 27 ) 28 29 func newCountTask() (func(), *int64) { 30 var cnt int64 31 return func() { 32 atomic.AddInt64(&cnt, 1) 33 }, &cnt 34 } 35 36 func newIOTask() (func(), *int64) { 37 var cnt int64 38 return func() { 39 time.Sleep(700 * time.Microsecond) 40 }, &cnt 41 } 42 43 func newCPUTask() (func(), *int64) { 44 var cnt int64 45 return func() { 46 atomic.AddInt64(&cnt, int64(fib(22))) 47 }, &cnt 48 } 49 50 func newRandomTask() (func(), *int64) { 51 c := rand.Intn(4) 52 tasks := []func(){ 53 func() { _ = fib(rand.Intn(20)) }, 54 func() { t, _ := newCountTask(); t() }, 55 func() { runtime.Gosched() }, 56 func() { time.Sleep(time.Duration(rand.Int63n(100)) * time.Microsecond) }, 57 } 58 return tasks[c], nil 59 } 60 61 func TestTaskPoolSimple(t *testing.T) { 62 numCPU := runtime.NumCPU() 63 taskCnt := int64(numCPU * numCPU * 100) 64 65 tp := NewTaskPoolSimple(1) 66 67 task, cnt := newCountTask() 68 69 var wg sync.WaitGroup 70 for i := 0; i < numCPU*numCPU; i++ { 71 wg.Add(1) 72 go func() { 73 for j := 0; j < 100; j++ { 74 ok := tp.AddTask(task) 75 if !ok { 76 t.Log(j) 77 } 78 } 79 wg.Done() 80 }() 81 } 82 wg.Wait() 83 tp.Close() 84 85 cntValue := atomic.LoadInt64(cnt) 86 if taskCnt != cntValue { 87 t.Error("want ", taskCnt, " got ", cntValue) 88 } 89 } 90 91 func BenchmarkTaskPoolSimple_CountTask(b *testing.B) { 92 tp := NewTaskPoolSimple(runtime.NumCPU()) 93 94 b.Run(`AddTask`, func(b *testing.B) { 95 task, _ := newCountTask() 96 b.RunParallel(func(pb *testing.PB) { 97 for pb.Next() { 98 tp.AddTask(task) 99 } 100 }) 101 }) 102 103 b.Run(`AddTaskAlways`, func(b *testing.B) { 104 task, _ := newCountTask() 105 b.RunParallel(func(pb *testing.PB) { 106 for pb.Next() { 107 tp.AddTaskAlways(task) 108 } 109 }) 110 }) 111 } 112 113 func fib(n int) int { 114 if n < 3 { 115 return 1 116 } 117 return fib(n-1) + fib(n-2) 118 } 119 120 // cpu-intensive task 121 func BenchmarkTaskPoolSimple_CPUTask(b *testing.B) { 122 tp := NewTaskPoolSimple(runtime.NumCPU()) 123 124 b.Run(`fib`, func(b *testing.B) { 125 t, _ := newCPUTask() 126 b.RunParallel(func(pb *testing.PB) { 127 for pb.Next() { 128 t() 129 } 130 }) 131 }) 132 133 b.Run(`AddTask`, func(b *testing.B) { 134 task, _ := newCPUTask() 135 b.RunParallel(func(pb *testing.PB) { 136 for pb.Next() { 137 tp.AddTask(task) 138 } 139 }) 140 }) 141 142 b.Run(`AddTaskAlways`, func(b *testing.B) { 143 task, _ := newCPUTask() 144 b.RunParallel(func(pb *testing.PB) { 145 for pb.Next() { 146 tp.AddTaskAlways(task) 147 } 148 }) 149 }) 150 } 151 152 // IO-intensive task 153 func BenchmarkTaskPoolSimple_IOTask(b *testing.B) { 154 tp := NewTaskPoolSimple(runtime.NumCPU()) 155 156 b.Run(`AddTask`, func(b *testing.B) { 157 task, _ := newIOTask() 158 b.RunParallel(func(pb *testing.PB) { 159 for pb.Next() { 160 tp.AddTask(task) 161 } 162 }) 163 }) 164 165 b.Run(`AddTaskAlways`, func(b *testing.B) { 166 task, _ := newIOTask() 167 b.RunParallel(func(pb *testing.PB) { 168 for pb.Next() { 169 tp.AddTaskAlways(task) 170 } 171 }) 172 }) 173 } 174 175 func BenchmarkTaskPoolSimple_RandomTask(b *testing.B) { 176 tp := NewTaskPoolSimple(runtime.NumCPU()) 177 178 b.Run(`AddTask`, func(b *testing.B) { 179 b.RunParallel(func(pb *testing.PB) { 180 for pb.Next() { 181 task, _ := newRandomTask() 182 tp.AddTask(task) 183 } 184 }) 185 }) 186 187 b.Run(`AddTaskAlways`, func(b *testing.B) { 188 b.RunParallel(func(pb *testing.PB) { 189 for pb.Next() { 190 task, _ := newRandomTask() 191 tp.AddTaskAlways(task) 192 } 193 }) 194 }) 195 } 196 197 func TestTaskPool(t *testing.T) { 198 numCPU := runtime.NumCPU() 199 // taskCnt := int64(numCPU * numCPU * 100) 200 201 tp := NewTaskPool( 202 WithTaskPoolTaskPoolSize(1), 203 WithTaskPoolTaskQueueNumber(1), 204 WithTaskPoolTaskQueueLength(1), 205 ) 206 207 // task, cnt := newCountTask() 208 task, _ := newCountTask() 209 210 var wg sync.WaitGroup 211 for i := 0; i < numCPU*numCPU; i++ { 212 wg.Add(1) 213 go func() { 214 for j := 0; j < 100; j++ { 215 ok := tp.AddTask(task) 216 if !ok { 217 t.Log(j) 218 } 219 } 220 wg.Done() 221 }() 222 } 223 wg.Wait() 224 tp.Close() 225 226 //if taskCnt != atomic.LoadInt64(cnt) { 227 // //t.Error("want ", taskCnt, " got ", *cnt) 228 //} 229 } 230 231 func BenchmarkTaskPool_CountTask(b *testing.B) { 232 tp := NewTaskPool( 233 WithTaskPoolTaskPoolSize(100), 234 WithTaskPoolTaskQueueNumber(runtime.NumCPU()), 235 // WithTaskPoolTaskQueueLength(runtime.NumCPU()), 236 ) 237 238 b.Run(`AddTask`, func(b *testing.B) { 239 task, _ := newCountTask() 240 b.RunParallel(func(pb *testing.PB) { 241 for pb.Next() { 242 tp.AddTask(task) 243 } 244 }) 245 }) 246 247 b.Run(`AddTaskAlways`, func(b *testing.B) { 248 task, _ := newCountTask() 249 b.RunParallel(func(pb *testing.PB) { 250 for pb.Next() { 251 tp.AddTaskAlways(task) 252 } 253 }) 254 }) 255 256 b.Run(`AddTaskBalance`, func(b *testing.B) { 257 task, _ := newCountTask() 258 b.RunParallel(func(pb *testing.PB) { 259 for pb.Next() { 260 tp.AddTaskBalance(task) 261 } 262 }) 263 }) 264 } 265 266 // cpu-intensive task 267 func BenchmarkTaskPool_CPUTask(b *testing.B) { 268 tp := NewTaskPool( 269 WithTaskPoolTaskPoolSize(100), 270 WithTaskPoolTaskQueueNumber(runtime.NumCPU()), 271 // WithTaskPoolTaskQueueLength(runtime.NumCPU()), 272 ) 273 274 b.Run(`fib`, func(b *testing.B) { 275 t, _ := newCPUTask() 276 b.RunParallel(func(pb *testing.PB) { 277 for pb.Next() { 278 t() 279 } 280 }) 281 }) 282 283 b.Run(`AddTask`, func(b *testing.B) { 284 task, _ := newCPUTask() 285 b.RunParallel(func(pb *testing.PB) { 286 for pb.Next() { 287 tp.AddTask(task) 288 } 289 }) 290 }) 291 292 b.Run(`AddTaskAlways`, func(b *testing.B) { 293 task, _ := newCPUTask() 294 b.RunParallel(func(pb *testing.PB) { 295 for pb.Next() { 296 tp.AddTaskAlways(task) 297 } 298 }) 299 }) 300 301 b.Run(`AddTaskBalance`, func(b *testing.B) { 302 task, _ := newCPUTask() 303 b.RunParallel(func(pb *testing.PB) { 304 for pb.Next() { 305 tp.AddTaskBalance(task) 306 } 307 }) 308 }) 309 } 310 311 // IO-intensive task 312 func BenchmarkTaskPool_IOTask(b *testing.B) { 313 tp := NewTaskPool( 314 WithTaskPoolTaskPoolSize(100), 315 WithTaskPoolTaskQueueNumber(runtime.NumCPU()), 316 // WithTaskPoolTaskQueueLength(runtime.NumCPU()), 317 ) 318 319 b.Run(`AddTask`, func(b *testing.B) { 320 task, _ := newIOTask() 321 b.RunParallel(func(pb *testing.PB) { 322 for pb.Next() { 323 tp.AddTask(task) 324 } 325 }) 326 }) 327 328 b.Run(`AddTaskAlways`, func(b *testing.B) { 329 task, _ := newIOTask() 330 b.RunParallel(func(pb *testing.PB) { 331 for pb.Next() { 332 tp.AddTaskAlways(task) 333 } 334 }) 335 }) 336 337 b.Run(`AddTaskBalance`, func(b *testing.B) { 338 task, _ := newIOTask() 339 b.RunParallel(func(pb *testing.PB) { 340 for pb.Next() { 341 tp.AddTaskBalance(task) 342 } 343 }) 344 }) 345 } 346 347 func BenchmarkTaskPool_RandomTask(b *testing.B) { 348 tp := NewTaskPool( 349 WithTaskPoolTaskPoolSize(100), 350 WithTaskPoolTaskQueueNumber(runtime.NumCPU()), 351 // WithTaskPoolTaskQueueLength(runtime.NumCPU()), 352 ) 353 354 b.Run(`AddTask`, func(b *testing.B) { 355 b.RunParallel(func(pb *testing.PB) { 356 for pb.Next() { 357 task, _ := newRandomTask() 358 tp.AddTask(task) 359 } 360 }) 361 }) 362 363 b.Run(`AddTaskAlways`, func(b *testing.B) { 364 b.RunParallel(func(pb *testing.PB) { 365 for pb.Next() { 366 task, _ := newRandomTask() 367 tp.AddTaskAlways(task) 368 } 369 }) 370 }) 371 372 b.Run(`AddTaskBalance`, func(b *testing.B) { 373 b.RunParallel(func(pb *testing.PB) { 374 for pb.Next() { 375 task, _ := newRandomTask() 376 tp.AddTaskBalance(task) 377 } 378 }) 379 }) 380 } 381 382 func PrintMemUsage(t *testing.T, prefix string) { 383 var m runtime.MemStats 384 runtime.ReadMemStats(&m) 385 t.Logf("%s Alloc = %v MiB", prefix, bToMb(m.Alloc)) 386 t.Logf("\tTotalAlloc = %v MiB", bToMb(m.TotalAlloc)) 387 t.Logf("\tSys = %v MiB", bToMb(m.Sys)) 388 t.Logf("\tNumGC = %v\n", m.NumGC) 389 } 390 391 func elapsed(t *testing.T, what string) func() { 392 start := time.Now() 393 return func() { 394 t.Logf("\n\t %s took %v\n", what, time.Since(start)) 395 } 396 } 397 398 func bToMb(b uint64) uint64 { 399 return b / 1024 / 1024 400 } 401 402 var n = 100000 403 404 func TestWithoutPool(t *testing.T) { 405 PrintMemUsage(t, "Before") 406 numG := runtime.NumGoroutine() 407 defer elapsed(t, "TestWithoutPool")() 408 var wg sync.WaitGroup 409 task, _ := newIOTask() 410 for i := 0; i < n; i++ { 411 wg.Add(1) 412 go func() { 413 task() 414 wg.Done() 415 }() 416 } 417 t.Logf("TestWithoutPool took %v goroutines\n", runtime.NumGoroutine()-numG) 418 wg.Wait() 419 PrintMemUsage(t, "After") 420 } 421 422 func TestWithSimpledPoolUseAlways(t *testing.T) { 423 PrintMemUsage(t, "Before") 424 numG := runtime.NumGoroutine() 425 defer elapsed(t, "TestWithSimplePool")() 426 tp := NewTaskPoolSimple(1000) 427 task, _ := newIOTask() 428 for i := 0; i < n; i++ { 429 tp.AddTaskAlways(task) 430 } 431 t.Logf("TestWithSimplePool took %v goroutines\n", runtime.NumGoroutine()-numG) 432 tp.Close() 433 PrintMemUsage(t, "After") 434 } 435 436 func TestWithSimplePool(t *testing.T) { 437 PrintMemUsage(t, "Before") 438 numG := runtime.NumGoroutine() 439 defer elapsed(t, "TestWithSimplePool")() 440 tp := NewTaskPoolSimple(1000) 441 task, _ := newIOTask() 442 for i := 0; i < n; i++ { 443 tp.AddTask(task) 444 } 445 t.Logf("TestWithSimplePool took %v goroutines\n", runtime.NumGoroutine()-numG) 446 tp.Close() 447 PrintMemUsage(t, "After") 448 } 449 450 func TestWithPool(t *testing.T) { 451 PrintMemUsage(t, "Before") 452 numG := runtime.NumGoroutine() 453 defer elapsed(t, "TestWithPool")() 454 tp := NewTaskPool( 455 WithTaskPoolTaskPoolSize(1000), 456 WithTaskPoolTaskQueueNumber(2), 457 // WithTaskPoolTaskQueueLength(runtime.NumCPU()), 458 ) 459 task, _ := newIOTask() 460 for i := 0; i < n; i++ { 461 tp.AddTask(task) 462 } 463 t.Logf("TestWithPool took %v goroutines\n", runtime.NumGoroutine()-numG) 464 tp.Close() 465 PrintMemUsage(t, "After") 466 } 467 468 func TestWithPoolUseAlways(t *testing.T) { 469 PrintMemUsage(t, "Before") 470 numG := runtime.NumGoroutine() 471 defer elapsed(t, "TestWithPoolUseAlways")() 472 tp := NewTaskPool( 473 WithTaskPoolTaskPoolSize(1000), 474 WithTaskPoolTaskQueueNumber(10), 475 // WithTaskPoolTaskQueueLength(runtime.NumCPU()), 476 ) 477 task, _ := newIOTask() 478 for i := 0; i < n; i++ { 479 tp.AddTaskAlways(task) 480 } 481 t.Logf("TestWithPoolUseAlways took %v goroutines\n", runtime.NumGoroutine()-numG) 482 tp.Close() 483 PrintMemUsage(t, "After") 484 } 485 486 /* 487 goos: darwin 488 goarch: amd64 489 pkg: github.com/dubbogo/gost/sync 执行次数 单次执行时间 单次执行内存消耗 单次执行内存分配次数 490 BenchmarkTaskPoolSimple_CountTask/AddTask-8 1693192 700 ns/op 0 B/op 0 allocs/op 491 BenchmarkTaskPoolSimple_CountTask/AddTaskAlways-8 3262932 315 ns/op 0 B/op 0 allocs/op 492 BenchmarkTaskPoolSimple_CPUTask/fib-8 83479 14760 ns/op 0 B/op 0 allocs/op 493 BenchmarkTaskPoolSimple_CPUTask/AddTask-8 85956 14571 ns/op 0 B/op 0 allocs/op 494 BenchmarkTaskPoolSimple_CPUTask/AddTaskAlways-8 1000000 17712 ns/op 19 B/op 0 allocs/op 495 BenchmarkTaskPoolSimple_IOTask/AddTask-8 10000 107361 ns/op 0 B/op 0 allocs/op 496 BenchmarkTaskPoolSimple_IOTask/AddTaskAlways-8 2772476 477 ns/op 79 B/op 1 allocs/op 497 BenchmarkTaskPoolSimple_RandomTask/AddTask-8 499417 2451 ns/op 6 B/op 0 allocs/op 498 BenchmarkTaskPoolSimple_RandomTask/AddTaskAlways-8 3307748 354 ns/op 21 B/op 0 allocs/op 499 500 BenchmarkTaskPool_CountTask/AddTask-8 5367189 229 ns/op 0 B/op 0 allocs/op 501 BenchmarkTaskPool_CountTask/AddTaskAlways-8 5438667 218 ns/op 0 B/op 0 allocs/op 502 BenchmarkTaskPool_CountTask/AddTaskBalance-8 4765616 247 ns/op 0 B/op 0 allocs/op 503 BenchmarkTaskPool_CPUTask/fib-8 74749 17153 ns/op 0 B/op 0 allocs/op 504 BenchmarkTaskPool_CPUTask/AddTask-8 71020 18131 ns/op 0 B/op 0 allocs/op 505 BenchmarkTaskPool_CPUTask/AddTaskAlways-8 563931 17725 ns/op 0 B/op 0 allocs/op 506 BenchmarkTaskPool_CPUTask/AddTaskBalance-8 204085 17720 ns/op 0 B/op 0 allocs/op 507 BenchmarkTaskPool_IOTask/AddTask-8 12427 106108 ns/op 0 B/op 0 allocs/op 508 BenchmarkTaskPool_IOTask/AddTaskAlways-8 2607068 504 ns/op 81 B/op 1 allocs/op 509 BenchmarkTaskPool_IOTask/AddTaskBalance-8 2065213 580 ns/op 63 B/op 0 allocs/op 510 BenchmarkTaskPool_RandomTask/AddTask-8 590595 2274 ns/op 6 B/op 0 allocs/op 511 BenchmarkTaskPool_RandomTask/AddTaskAlways-8 3565921 333 ns/op 21 B/op 0 allocs/op 512 BenchmarkTaskPool_RandomTask/AddTaskBalance-8 1487217 839 ns/op 17 B/op 0 allocs/op 513 PASS 514 515 516 */