github.com/alphadose/zenq/v2@v2.8.4/benchmarks/e2e/benchmark_test.go (about) 1 package zenq_test 2 3 import ( 4 "fmt" 5 "runtime" 6 "sync" 7 "testing" 8 9 "github.com/alphadose/zenq/v2" 10 ) 11 12 const bufferSize = 1 << 12 13 14 type Payload struct { 15 first byte 16 second int64 17 third float64 18 fourth string 19 fifth complex64 20 sixth []rune 21 seventh bool 22 } 23 24 type test struct { 25 writers int 26 readers int 27 inputSize int 28 } 29 30 var testCases = []test{ 31 {writers: 1, readers: 1, inputSize: 1e3}, 32 {writers: 3, readers: 3, inputSize: 3e3}, 33 {writers: 8, readers: 8, inputSize: 8e3}, 34 {writers: bufferSize * 2, readers: 1, inputSize: bufferSize * 2 * 4}, 35 {writers: 1, readers: bufferSize * 2, inputSize: bufferSize * 2 * 4}, 36 {writers: 100, readers: 100, inputSize: 6e6}, 37 {writers: 1e3, readers: 1e3, inputSize: 7e6}, 38 } 39 40 func init() { 41 for _, t := range testCases { 42 if t.inputSize%t.writers != 0 { 43 panic(fmt.Sprintf("input size %d should be dividable by writers %d", t.inputSize, t.writers)) 44 } 45 if t.inputSize%t.readers != 0 { 46 panic(fmt.Sprintf("input size %d should be dividable by readers %d", t.inputSize, t.readers)) 47 } 48 } 49 } 50 51 func BenchmarkChan_ProduceConsume(b *testing.B) { 52 for _, t := range testCases { 53 t := t 54 b.Run(fmt.Sprintf("W%d/R%d/Size%d", t.writers, t.readers, t.inputSize), func(b *testing.B) { 55 for i := 0; i < b.N; i++ { 56 benchmarkProduceConsumeChan(b, t) 57 } 58 }) 59 } 60 } 61 62 func benchmarkProduceConsumeChan(b *testing.B, t test) { 63 q := make(chan Payload, bufferSize) 64 defer runtime.KeepAlive(q) 65 66 writesPerProducer := t.inputSize / t.writers 67 readsPerConsumer := t.inputSize / t.readers 68 69 var wg sync.WaitGroup 70 wg.Add(t.writers) 71 72 // b.ResetTimer() 73 74 for writer := 0; writer < t.writers; writer++ { 75 go func() { 76 defer wg.Done() 77 for i := 0; i < writesPerProducer; i++ { 78 q <- Payload{} 79 } 80 }() 81 } 82 83 wg.Add(t.readers) 84 for reader := 0; reader < t.readers; reader++ { 85 go func() { 86 defer wg.Done() 87 for i := 0; i < readsPerConsumer; i++ { 88 <-q 89 } 90 }() 91 } 92 93 wg.Wait() 94 } 95 96 func BenchmarkZenQ_ProduceConsume(b *testing.B) { 97 for _, t := range testCases { 98 t := t 99 b.Run(fmt.Sprintf("W%d/R%d/Size%d", t.writers, t.readers, t.inputSize), func(b *testing.B) { 100 for i := 0; i < b.N; i++ { 101 benchmarkProduceConsumeZenQ(b, t) 102 } 103 }) 104 } 105 } 106 107 func benchmarkProduceConsumeZenQ(b *testing.B, t test) { 108 q := zenq.New[Payload](bufferSize) 109 defer runtime.KeepAlive(q) 110 111 writesPerProducer := t.inputSize / t.writers 112 readsPerConsumer := t.inputSize / t.readers 113 114 var wg sync.WaitGroup 115 wg.Add(t.writers) 116 117 // b.ResetTimer() 118 119 for writer := 0; writer < t.writers; writer++ { 120 go func() { 121 defer wg.Done() 122 for i := 0; i < writesPerProducer; i++ { 123 q.Write(Payload{}) 124 } 125 }() 126 } 127 128 wg.Add(t.readers) 129 for reader := 0; reader < t.readers; reader++ { 130 go func() { 131 defer wg.Done() 132 for i := 0; i < readsPerConsumer; i++ { 133 q.Read() 134 } 135 }() 136 } 137 138 wg.Wait() 139 } 140 141 func BenchmarkChan_New(b *testing.B) { 142 b.Run("struct{}", func(b *testing.B) { 143 b.ReportAllocs() 144 for i := 0; i < b.N; i++ { 145 ch := make(chan struct{}, bufferSize) 146 runtime.KeepAlive(ch) 147 } 148 }) 149 b.Run("byte", func(b *testing.B) { 150 b.ReportAllocs() 151 for i := 0; i < b.N; i++ { 152 ch := make(chan byte, bufferSize) 153 runtime.KeepAlive(ch) 154 } 155 }) 156 b.Run("int64", func(b *testing.B) { 157 b.ReportAllocs() 158 for i := 0; i < b.N; i++ { 159 ch := make(chan int64, bufferSize) 160 runtime.KeepAlive(ch) 161 } 162 }) 163 } 164 165 func BenchmarkZenQ_New(b *testing.B) { 166 b.Run("struct{}", func(b *testing.B) { 167 b.ReportAllocs() 168 for i := 0; i < b.N; i++ { 169 zq := zenq.New[struct{}](bufferSize) 170 runtime.KeepAlive(zq) 171 } 172 }) 173 b.Run("byte", func(b *testing.B) { 174 b.ReportAllocs() 175 for i := 0; i < b.N; i++ { 176 zq := zenq.New[byte](bufferSize) 177 runtime.KeepAlive(zq) 178 } 179 }) 180 b.Run("int64", func(b *testing.B) { 181 b.ReportAllocs() 182 for i := 0; i < b.N; i++ { 183 zq := zenq.New[int64](bufferSize) 184 runtime.KeepAlive(zq) 185 } 186 }) 187 } 188 189 func BenchmarkZenQ_BackgroundSelectWait(b *testing.B) { 190 const N = 1e4 191 q := zenq.New[struct{}](bufferSize) 192 193 // create background waiters 194 for i := 0; i < N; i++ { 195 go func() { 196 alt := zenq.New[struct{}](bufferSize) 197 zenq.Select(q, alt) 198 }() 199 } 200 201 b.ResetTimer() 202 203 a := zenq.New[int](bufferSize) 204 for i := 0; i < b.N; i++ { 205 a.Write(i) 206 runtime.Gosched() 207 a.Read() 208 } 209 210 // release background waiters 211 for i := 0; i < N; i++ { 212 q.Write(struct{}{}) 213 } 214 } 215 216 func BenchmarkChan_BackgroundSelectWait(b *testing.B) { 217 const N = 1e4 218 q := make(chan struct{}) 219 220 // create background waiters 221 for i := 0; i < N; i++ { 222 go func() { 223 x := make(chan struct{}) 224 select { 225 case <-q: 226 case <-x: 227 } 228 }() 229 } 230 231 b.ResetTimer() 232 233 a := make(chan int, bufferSize) 234 for i := 0; i < b.N; i++ { 235 a <- i 236 runtime.Gosched() 237 <-a 238 } 239 240 // release background waiters 241 for i := 0; i < N; i++ { 242 q <- struct{}{} 243 } 244 }