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  */