github.com/hxx258456/ccgo@v0.0.5-0.20230213014102-48b35f46f66f/grpc/internal/profiling/buffer/buffer_test.go (about) 1 /* 2 * 3 * Copyright 2019 gRPC authors. 4 * 5 * Licensed under the Apache License, Version 2.0 (the "License"); 6 * you may not use this file except in compliance with the License. 7 * 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 19 package buffer 20 21 import ( 22 "fmt" 23 "sync" 24 "testing" 25 "time" 26 27 "github.com/hxx258456/ccgo/grpc/internal/grpctest" 28 ) 29 30 type s struct { 31 grpctest.Tester 32 } 33 34 func Test(t *testing.T) { 35 grpctest.RunSubTests(t, s{}) 36 } 37 38 func (s) TestCircularBufferSerial(t *testing.T) { 39 var size, i uint32 40 var result []interface{} 41 42 size = 1 << 15 43 cb, err := NewCircularBuffer(size) 44 if err != nil { 45 t.Fatalf("error allocating CircularBuffer: %v", err) 46 } 47 48 for i = 0; i < size/2; i++ { 49 cb.Push(i) 50 } 51 52 result = cb.Drain() 53 if uint32(len(result)) != size/2 { 54 t.Fatalf("len(result) = %d; want %d", len(result), size/2) 55 } 56 57 // The returned result isn't necessarily sorted. 58 seen := make(map[uint32]bool) 59 for _, r := range result { 60 seen[r.(uint32)] = true 61 } 62 63 for i = 0; i < uint32(len(result)); i++ { 64 if !seen[i] { 65 t.Fatalf("seen[%d] = false; want true", i) 66 } 67 } 68 69 for i = 0; i < size; i++ { 70 cb.Push(i) 71 } 72 73 result = cb.Drain() 74 if uint32(len(result)) != size { 75 t.Fatalf("len(result) = %d; want %d", len(result), size/2) 76 } 77 } 78 79 func (s) TestCircularBufferOverflow(t *testing.T) { 80 var size, i uint32 81 var result []interface{} 82 83 size = 1 << 10 84 cb, err := NewCircularBuffer(size) 85 if err != nil { 86 t.Fatalf("error allocating CircularBuffer: %v", err) 87 } 88 89 for i = 0; i < 10*size; i++ { 90 cb.Push(i) 91 } 92 93 result = cb.Drain() 94 95 if uint32(len(result)) != size { 96 t.Fatalf("len(result) = %d; want %d", len(result), size) 97 } 98 99 for idx, x := range result { 100 if x.(uint32) < size { 101 t.Fatalf("result[%d] = %d; want it to be >= %d", idx, x, size) 102 } 103 } 104 } 105 106 func (s) TestCircularBufferConcurrent(t *testing.T) { 107 for tn := 0; tn < 2; tn++ { 108 var size uint32 109 var result []interface{} 110 111 size = 1 << 6 112 cb, err := NewCircularBuffer(size) 113 if err != nil { 114 t.Fatalf("error allocating CircularBuffer: %v", err) 115 } 116 117 type item struct { 118 R uint32 119 N uint32 120 T time.Time 121 } 122 123 var wg sync.WaitGroup 124 for r := uint32(0); r < 1024; r++ { 125 wg.Add(1) 126 go func(r uint32) { 127 for n := uint32(0); n < size; n++ { 128 cb.Push(item{R: r, N: n, T: time.Now()}) 129 } 130 wg.Done() 131 }(r) 132 } 133 134 // Wait for all goroutines to finish only in one test. Draining 135 // concurrently while Pushes are still happening will test for races in the 136 // Draining lock. 137 if tn == 0 { 138 wg.Wait() 139 } 140 141 result = cb.Drain() 142 143 // Can't expect the buffer to be full if the Pushes aren't necessarily done. 144 if tn == 0 { 145 if uint32(len(result)) != size { 146 t.Fatalf("len(result) = %d; want %d", len(result), size) 147 } 148 } 149 150 // There can be absolutely no expectation on the order of the data returned 151 // by Drain because: (a) everything is happening concurrently (b) a 152 // round-robin is used to write to different queues (and therefore 153 // different cachelines) for less write contention. 154 155 // Wait for all goroutines to complete before moving on to other tests. If 156 // the benchmarks run after this, it might affect performance unfairly. 157 wg.Wait() 158 } 159 } 160 161 func BenchmarkCircularBuffer(b *testing.B) { 162 x := 1 163 for size := 1 << 16; size <= 1<<20; size <<= 1 { 164 for routines := 1; routines <= 1<<8; routines <<= 1 { 165 b.Run(fmt.Sprintf("goroutines:%d/size:%d", routines, size), func(b *testing.B) { 166 cb, err := NewCircularBuffer(uint32(size)) 167 if err != nil { 168 b.Fatalf("error allocating CircularBuffer: %v", err) 169 } 170 171 perRoutine := b.N / routines 172 var wg sync.WaitGroup 173 for r := 0; r < routines; r++ { 174 wg.Add(1) 175 go func() { 176 for i := 0; i < perRoutine; i++ { 177 cb.Push(&x) 178 } 179 wg.Done() 180 }() 181 } 182 wg.Wait() 183 }) 184 } 185 } 186 }