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)