github.com/angenalZZZ/gofunc@v0.0.0-20210507121333-48ff1be3917b/f/chan_test.go (about) 1 package f_test 2 3 import ( 4 "fmt" 5 "github.com/angenalZZZ/gofunc/f" 6 "os" 7 "strings" 8 "sync" 9 "testing" 10 "time" 11 ) 12 13 var benchmarks bool 14 15 func init() { 16 benchmarks = os.Getenv("CHANNEL_BENCH") == "1" 17 if !benchmarks { 18 fmt.Printf("Use CHANNEL_BENCH=1 for benchmarks\n") 19 } 20 } 21 22 func TestOrder(t *testing.T) { 23 // testing that order is preserved 24 type msgT struct{ i, thread int } 25 ch := f.MakeChannel(0) 26 N := 1000000 27 T := 100 28 go func() { 29 f.Ops(N, 100, func(i, thread int) { 30 if !ch.Send(&msgT{i, thread}) { 31 panic("bad news") 32 } 33 }) 34 ch.Close() 35 }() 36 // create unique buckets per thread and store each message 37 // sequentially in their respective bucket. 38 m := make(map[int][]int) 39 for { 40 v, ok := ch.Recv() 41 if !ok { 42 break 43 } 44 msg := v.(*msgT) 45 m[msg.thread] = append(m[msg.thread], msg.i) 46 } 47 // check that each bucket contains ordered data check for duplicates 48 all := make(map[int]bool) 49 for thread := 0; thread < T; thread++ { 50 b, ok := m[thread] 51 // println(thread, m[thread]) 52 // continue 53 if !ok { 54 t.Fatal("missing bucket") 55 } 56 if len(b) != N/T { 57 t.Fatal("invalid bucket size") 58 } 59 h := -1 60 for i := 0; i < len(b); i++ { 61 if b[i] <= h { 62 t.Fatal("out of order") 63 } 64 h = b[i] 65 if all[h] { 66 t.Fatal("duplicate value") 67 } 68 all[h] = true 69 } 70 } 71 } 72 73 func fixLeft(s string, n int) string { 74 return (s + strings.Repeat(" ", n))[:n] 75 } 76 func fixRight(s string, n int) string { 77 return (strings.Repeat(" ", n) + s)[len(s):] 78 } 79 80 func printResults(key string, N, P int, dur time.Duration) { 81 s := fixLeft(key, 13) + " " 82 s += fixLeft(fmt.Sprintf("%d ops in %dms", N, int(dur.Seconds()*1000)), 22) + " " 83 s += fixRight(fmt.Sprintf("%d/sec", int(float64(N)/dur.Seconds())), 12) + " " 84 s += fixRight(fmt.Sprintf("%dns/op", int(dur/time.Duration(N))), 10) + " " 85 s += fixRight(fmt.Sprintf("%s %4d producer", (s + strings.Repeat(" ", 100))[:60], P), 14) 86 fmt.Printf("%s\n", strings.TrimSpace(s)) 87 } 88 89 func TestChannelUnbuffered(t *testing.T) { 90 N := 1000000 91 for P := 1; P < 1000; P *= 10 { 92 start := time.Now() 93 benchmarkChannel(N, 0, P, false) 94 if benchmarks { 95 printResults("channel(0)", N, P, time.Since(start)) 96 } 97 } 98 } 99 100 func TestChannel10Unbuffered(t *testing.T) { 101 N := 1000000 102 for P := 1; P < 1000; P *= 10 { 103 start := time.Now() 104 benchmarkChannel(N, 10, P, false) 105 if benchmarks { 106 printResults("channel(10)", N, P, time.Since(start)) 107 } 108 } 109 } 110 111 func TestChannel100Unbuffered(t *testing.T) { 112 N := 1000000 113 for P := 1; P < 1000; P *= 10 { 114 start := time.Now() 115 benchmarkChannel(N, 100, P, false) 116 if benchmarks { 117 printResults("channel(100)", N, P, time.Since(start)) 118 } 119 } 120 } 121 122 func TestGoChanUnbuffered(t *testing.T) { 123 if !benchmarks { 124 return 125 } 126 N := 1000000 127 var start time.Time 128 for P := 1; P < 1000; P *= 10 { 129 start = time.Now() 130 benchmarkGoChan(N, 0, P, false) 131 printResults("go-chan(0)", N, P, time.Since(start)) 132 } 133 } 134 135 func TestGoChan10(t *testing.T) { 136 if !benchmarks { 137 return 138 } 139 N := 1000000 140 var start time.Time 141 for P := 1; P < 1000; P *= 10 { 142 start = time.Now() 143 benchmarkGoChan(N, 10, P, false) 144 printResults("go-chan(10)", N, P, time.Since(start)) 145 } 146 } 147 148 func TestGoChan100(t *testing.T) { 149 if !benchmarks { 150 return 151 } 152 N := 1000000 153 var start time.Time 154 for P := 1; P < 1000; P *= 10 { 155 start = time.Now() 156 benchmarkGoChan(N, 100, P, false) 157 printResults("go-chan(100)", N, P, time.Since(start)) 158 } 159 } 160 161 func benchmarkChannel(N int, buffered int, P int, validate bool) { 162 ch := f.MakeChannel(buffered) 163 var wg sync.WaitGroup 164 wg.Add(1) 165 go func() { 166 for i := 0; i < N; i++ { 167 v, _ := ch.Recv() 168 if validate { 169 if v != uint64(i) { 170 panic("out of order") 171 } 172 } 173 } 174 wg.Done() 175 }() 176 f.Ops(N, P, func(i, _ int) { 177 ch.Send(uint64(i)) 178 }) 179 wg.Wait() 180 } 181 182 func benchmarkGoChan(N, buffered int, producers int, validate bool) { 183 ch := make(chan uint64, buffered) 184 var wg sync.WaitGroup 185 wg.Add(1) 186 go func() { 187 for i := 0; i < N; i++ { 188 v := <-ch 189 if validate { 190 if v != uint64(i) { 191 panic("out of order") 192 } 193 } 194 } 195 wg.Done() 196 }() 197 f.Ops(N, producers, func(i, _ int) { 198 ch <- uint64(i) 199 }) 200 wg.Wait() 201 } 202 203 func Benchmark100ProducerChannel100(b *testing.B) { 204 b.ReportAllocs() 205 benchmarkChannel(b.N, 100, 100, false) 206 } 207 208 func Benchmark100ProducerChannel10(b *testing.B) { 209 b.ReportAllocs() 210 benchmarkChannel(b.N, 10, 100, false) 211 } 212 213 func Benchmark100ProducerChannelUnbuffered(b *testing.B) { 214 b.ReportAllocs() 215 benchmarkChannel(b.N, 0, 100, false) 216 } 217 218 func Benchmark100ProducerGoChan100(b *testing.B) { 219 b.ReportAllocs() 220 benchmarkGoChan(b.N, 100, 100, false) 221 } 222 223 func Benchmark100ProducerGoChan10(b *testing.B) { 224 b.ReportAllocs() 225 benchmarkGoChan(b.N, 10, 100, false) 226 } 227 228 func Benchmark100ProducerGoChanUnbuffered(b *testing.B) { 229 b.ReportAllocs() 230 benchmarkGoChan(b.N, 0, 100, false) 231 }