github.com/alphadose/itogami@v0.4.1-0.20221016160904-c25d0a36bfe7/README.md (about)

     1  # Itogami
     2  
     3  > An experimental goroutine pool implemented using a lock-free stack
     4  
     5  By limiting concurrency with a fixed pool size and recycling goroutines using a stack, itogami saves a lot of memory as compared to using unlimited goroutines and remaining just as fast.
     6  
     7  Benchmarks to support the above claims [here](#benchmarks)
     8  
     9  **Note:- This work is experimental and should not be used in production**
    10  
    11  ## Installation
    12  
    13  You need Golang [1.19.x](https://go.dev/dl/) or above
    14  
    15  ```bash
    16  $ go get github.com/alphadose/itogami
    17  ```
    18  
    19  ## Usage
    20  
    21  ```go
    22  package main
    23  
    24  import (
    25  	"fmt"
    26  	"sync"
    27  	"sync/atomic"
    28  	"time"
    29  
    30  	"github.com/alphadose/itogami"
    31  )
    32  
    33  const runTimes uint32 = 1000
    34  
    35  var sum uint32
    36  
    37  func myFunc(i uint32) {
    38  	atomic.AddUint32(&sum, i)
    39  	fmt.Printf("run with %d\n", i)
    40  }
    41  
    42  func demoFunc() {
    43  	time.Sleep(10 * time.Millisecond)
    44  	println("Hello World")
    45  }
    46  
    47  func examplePool() {
    48  	var wg sync.WaitGroup
    49  	// Use the common pool
    50  	pool := itogami.NewPool(10)
    51  
    52  	syncCalculateSum := func() {
    53  		demoFunc()
    54  		wg.Done()
    55  	}
    56  	for i := uint32(0); i < runTimes; i++ {
    57  		wg.Add(1)
    58  		// Submit task to the pool
    59  		pool.Submit(syncCalculateSum)
    60  	}
    61  	wg.Wait()
    62  	println("finished all tasks")
    63  }
    64  
    65  func examplePoolWithFunc() {
    66  	var wg sync.WaitGroup
    67  	// Use the pool with a pre-defined function
    68  	pool := itogami.NewPoolWithFunc(10, func(i uint32) {
    69  		myFunc(i)
    70  		wg.Done()
    71  	})
    72  	for i := uint32(0); i < runTimes; i++ {
    73  		wg.Add(1)
    74  		// Invoke the function with a value
    75  		pool.Invoke(i)
    76  	}
    77  	wg.Wait()
    78  	fmt.Printf("finish all tasks, result is %d\n", sum)
    79  }
    80  
    81  func main() {
    82  	examplePool()
    83  	examplePoolWithFunc()
    84  }
    85  ```
    86  
    87  ## Benchmarks
    88  
    89  Benchmarking was performed against:-
    90  
    91  1. Unlimited goroutines
    92  2. [Ants](https://github.com/panjf2000/ants)
    93  3. [Gamma-Zero-Worker-Pool](https://github.com/gammazero/workerpool)
    94  4. [golang.org/x/sync/errgroup](https://pkg.go.dev/golang.org/x/sync/errgroup)
    95  5. [Bytedance GoPool](https://github.com/bytedance/gopkg/tree/develop/util/gopool)
    96  
    97  Pool size -> 50k
    98  
    99  CPU -> M1, arm64, 8 cores, 3.2 GHz
   100  
   101  OS -> darwin
   102  
   103  Results were computed from [benchstat](https://pkg.go.dev/golang.org/x/perf/cmd/benchstat) of 30 cases
   104  ```
   105  name                   time/op
   106  UnlimitedGoroutines-8   331ms ± 4%
   107  ErrGroup-8              515ms ± 9%
   108  AntsPool-8              582ms ± 9%
   109  GammaZeroPool-8         740ms ±13%
   110  BytedanceGoPool-8       572ms ±18%
   111  ItogamiPool-8           337ms ± 1%
   112  
   113  name                   alloc/op
   114  UnlimitedGoroutines-8  96.3MB ± 0%
   115  ErrGroup-8              120MB ± 0%
   116  AntsPool-8             22.4MB ± 6%
   117  GammaZeroPool-8        18.8MB ± 1%
   118  BytedanceGoPool-8      82.2MB ± 2%
   119  ItogamiPool-8          25.6MB ± 2%
   120  
   121  name                   allocs/op
   122  UnlimitedGoroutines-8   2.00M ± 0%
   123  ErrGroup-8              3.00M ± 0%
   124  AntsPool-8              1.10M ± 2%
   125  GammaZeroPool-8         1.08M ± 0%
   126  BytedanceGoPool-8       2.59M ± 1%
   127  ItogamiPool-8           1.08M ± 0%
   128  ```
   129  
   130  The following conclusions can be drawn from the above results:-
   131  
   132  1. [Itogami](https://github.com/alphadose/itogami) is the fastest among all goroutine pool implementations and slightly slower than unlimited goroutines
   133  2. [Itogami](https://github.com/alphadose/itogami) has the least `allocs/op` and hence the memory usage scales really well with high load
   134  3. The memory used per operation is in the acceptable range of other pools and drastically lower than unlimited goroutines
   135  4. The tolerance (± %) for [Itogami](https://github.com/alphadose/itogami) is quite low for all 3 metrics indicating that the algorithm is quite stable overall
   136  
   137  Benchmarking code available [here](https://github.com/alphadose/go-threadpool-benchmarks)