gvisor.dev/gvisor@v0.0.0-20240520182842-f9d4d51c7e0f/pkg/syncevent/broadcaster_test.go (about) 1 // Copyright 2020 The gVisor Authors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package syncevent 16 17 import ( 18 "fmt" 19 "math/rand" 20 "testing" 21 22 "gvisor.dev/gvisor/pkg/sync" 23 "gvisor.dev/gvisor/pkg/waiter" 24 ) 25 26 func TestBroadcasterFilter(t *testing.T) { 27 const numReceivers = 2 * MaxEvents 28 29 var br Broadcaster 30 ws := make([]Waiter, numReceivers) 31 for i := range ws { 32 ws[i].Init() 33 br.SubscribeEvents(ws[i].Receiver(), 1<<(i%MaxEvents)) 34 } 35 for ev := 0; ev < MaxEvents; ev++ { 36 br.Broadcast(1 << ev) 37 for i := range ws { 38 want := NoEvents 39 if i%MaxEvents == ev { 40 want = 1 << ev 41 } 42 if got := ws[i].Receiver().PendingAndAckAll(); got != want { 43 t.Errorf("after Broadcast of event %d: waiter %d has pending event set %#x, wanted %#x", ev, i, got, want) 44 } 45 } 46 } 47 } 48 49 // TestBroadcasterManySubscriptions tests that subscriptions are not lost by 50 // table expansion/compaction. 51 func TestBroadcasterManySubscriptions(t *testing.T) { 52 const numReceivers = 5000 // arbitrary 53 54 var br Broadcaster 55 ws := make([]Waiter, numReceivers) 56 for i := range ws { 57 ws[i].Init() 58 } 59 60 ids := make([]SubscriptionID, numReceivers) 61 for i := 0; i < numReceivers; i++ { 62 // Subscribe receiver i. 63 ids[i] = br.SubscribeEvents(ws[i].Receiver(), 1) 64 // Check that receivers [0, i] are subscribed. 65 br.Broadcast(1) 66 for j := 0; j <= i; j++ { 67 if ws[j].Pending() != 1 { 68 t.Errorf("receiver %d did not receive an event after subscription of receiver %d", j, i) 69 } 70 ws[j].Ack(1) 71 } 72 } 73 74 // Generate a random order for unsubscriptions. 75 unsub := rand.Perm(numReceivers) 76 for i := 0; i < numReceivers; i++ { 77 // Unsubscribe receiver unsub[i]. 78 br.UnsubscribeEvents(ids[unsub[i]]) 79 // Check that receivers [unsub[0], unsub[i]] are not subscribed, and that 80 // receivers (unsub[i], unsub[numReceivers]) are still subscribed. 81 br.Broadcast(1) 82 for j := 0; j <= i; j++ { 83 if ws[unsub[j]].Pending() != 0 { 84 t.Errorf("unsub iteration %d: receiver %d received an event after unsubscription of receiver %d", i, unsub[j], unsub[i]) 85 } 86 } 87 for j := i + 1; j < numReceivers; j++ { 88 if ws[unsub[j]].Pending() != 1 { 89 t.Errorf("unsub iteration %d: receiver %d did not receive an event after unsubscription of receiver %d", i, unsub[j], unsub[i]) 90 } 91 ws[unsub[j]].Ack(1) 92 } 93 } 94 } 95 96 var ( 97 receiverCountsNonZero = []int{1, 4, 16, 64} 98 receiverCountsIncludingZero = append([]int{0}, receiverCountsNonZero...) 99 ) 100 101 // BenchmarkBroadcasterX, BenchmarkMapX, and BenchmarkQueueX benchmark usage 102 // pattern X (described in terms of Broadcaster) with Broadcaster, a 103 // Mutex-protected map[*Receiver]Set, and waiter.Queue respectively. 104 105 // BenchmarkXxxSubscribeUnsubscribe measures the cost of a Subscribe/Unsubscribe 106 // cycle. 107 108 func BenchmarkBroadcasterSubscribeUnsubscribe(b *testing.B) { 109 var br Broadcaster 110 var w Waiter 111 w.Init() 112 113 b.ResetTimer() 114 for i := 0; i < b.N; i++ { 115 id := br.SubscribeEvents(w.Receiver(), 1) 116 br.UnsubscribeEvents(id) 117 } 118 } 119 120 func BenchmarkMapSubscribeUnsubscribe(b *testing.B) { 121 var mu sync.Mutex 122 m := make(map[*Receiver]Set) 123 var w Waiter 124 w.Init() 125 126 b.ResetTimer() 127 for i := 0; i < b.N; i++ { 128 mu.Lock() 129 m[w.Receiver()] = Set(1) 130 mu.Unlock() 131 mu.Lock() 132 delete(m, w.Receiver()) 133 mu.Unlock() 134 } 135 } 136 137 func BenchmarkQueueSubscribeUnsubscribe(b *testing.B) { 138 var q waiter.Queue 139 e, _ := waiter.NewChannelEntry(1) 140 141 b.ResetTimer() 142 for i := 0; i < b.N; i++ { 143 q.EventRegister(&e) 144 q.EventUnregister(&e) 145 } 146 } 147 148 // BenchmarkXxxSubscribeUnsubscribeBatch is similar to 149 // BenchmarkXxxSubscribeUnsubscribe, but subscribes and unsubscribes a large 150 // number of Receivers at a time in order to measure the amortized overhead of 151 // table expansion/compaction. (Since waiter.Queue is implemented using a 152 // linked list, BenchmarkQueueSubscribeUnsubscribe and 153 // BenchmarkQueueSubscribeUnsubscribeBatch should produce nearly the same 154 // result.) 155 156 const numBatchReceivers = 1000 157 158 func BenchmarkBroadcasterSubscribeUnsubscribeBatch(b *testing.B) { 159 var br Broadcaster 160 ws := make([]Waiter, numBatchReceivers) 161 for i := range ws { 162 ws[i].Init() 163 } 164 ids := make([]SubscriptionID, numBatchReceivers) 165 166 // Generate a random order for unsubscriptions. 167 unsub := rand.Perm(numBatchReceivers) 168 169 b.ResetTimer() 170 for i := 0; i < b.N/numBatchReceivers; i++ { 171 for j := 0; j < numBatchReceivers; j++ { 172 ids[j] = br.SubscribeEvents(ws[j].Receiver(), 1) 173 } 174 for j := 0; j < numBatchReceivers; j++ { 175 br.UnsubscribeEvents(ids[unsub[j]]) 176 } 177 } 178 } 179 180 func BenchmarkMapSubscribeUnsubscribeBatch(b *testing.B) { 181 var mu sync.Mutex 182 m := make(map[*Receiver]Set) 183 ws := make([]Waiter, numBatchReceivers) 184 for i := range ws { 185 ws[i].Init() 186 } 187 188 // Generate a random order for unsubscriptions. 189 unsub := rand.Perm(numBatchReceivers) 190 191 b.ResetTimer() 192 for i := 0; i < b.N/numBatchReceivers; i++ { 193 for j := 0; j < numBatchReceivers; j++ { 194 mu.Lock() 195 m[ws[j].Receiver()] = Set(1) 196 mu.Unlock() 197 } 198 for j := 0; j < numBatchReceivers; j++ { 199 mu.Lock() 200 delete(m, ws[unsub[j]].Receiver()) 201 mu.Unlock() 202 } 203 } 204 } 205 206 func BenchmarkQueueSubscribeUnsubscribeBatch(b *testing.B) { 207 var q waiter.Queue 208 es := make([]waiter.Entry, numBatchReceivers) 209 for i := range es { 210 es[i], _ = waiter.NewChannelEntry(1) 211 } 212 213 // Generate a random order for unsubscriptions. 214 unsub := rand.Perm(numBatchReceivers) 215 216 b.ResetTimer() 217 for i := 0; i < b.N/numBatchReceivers; i++ { 218 for j := 0; j < numBatchReceivers; j++ { 219 q.EventRegister(&es[j]) 220 } 221 for j := 0; j < numBatchReceivers; j++ { 222 q.EventUnregister(&es[unsub[j]]) 223 } 224 } 225 } 226 227 // BenchmarkXxxBroadcastRedundant measures how long it takes to Broadcast 228 // already-pending events to multiple Receivers. 229 230 func BenchmarkBroadcasterBroadcastRedundant(b *testing.B) { 231 for _, n := range receiverCountsIncludingZero { 232 b.Run(fmt.Sprintf("%d", n), func(b *testing.B) { 233 var br Broadcaster 234 ws := make([]Waiter, n) 235 for i := range ws { 236 ws[i].Init() 237 br.SubscribeEvents(ws[i].Receiver(), 1) 238 } 239 br.Broadcast(1) 240 241 b.ResetTimer() 242 for i := 0; i < b.N; i++ { 243 br.Broadcast(1) 244 } 245 }) 246 } 247 } 248 249 func BenchmarkMapBroadcastRedundant(b *testing.B) { 250 for _, n := range receiverCountsIncludingZero { 251 b.Run(fmt.Sprintf("%d", n), func(b *testing.B) { 252 var mu sync.Mutex 253 m := make(map[*Receiver]Set) 254 ws := make([]Waiter, n) 255 for i := range ws { 256 ws[i].Init() 257 m[ws[i].Receiver()] = Set(1) 258 } 259 mu.Lock() 260 for r := range m { 261 r.Notify(1) 262 } 263 mu.Unlock() 264 265 b.ResetTimer() 266 for i := 0; i < b.N; i++ { 267 mu.Lock() 268 for r := range m { 269 r.Notify(1) 270 } 271 mu.Unlock() 272 } 273 }) 274 } 275 } 276 277 func BenchmarkQueueBroadcastRedundant(b *testing.B) { 278 for _, n := range receiverCountsIncludingZero { 279 b.Run(fmt.Sprintf("%d", n), func(b *testing.B) { 280 var q waiter.Queue 281 for i := 0; i < n; i++ { 282 e, _ := waiter.NewChannelEntry(1) 283 q.EventRegister(&e) 284 } 285 q.Notify(1) 286 287 b.ResetTimer() 288 for i := 0; i < b.N; i++ { 289 q.Notify(1) 290 } 291 }) 292 } 293 } 294 295 // BenchmarkXxxBroadcastAck measures how long it takes to Broadcast events to 296 // multiple Receivers, check that all Receivers have received the event, and 297 // clear the event from all Receivers. 298 299 func BenchmarkBroadcasterBroadcastAck(b *testing.B) { 300 for _, n := range receiverCountsNonZero { 301 b.Run(fmt.Sprintf("%d", n), func(b *testing.B) { 302 var br Broadcaster 303 ws := make([]Waiter, n) 304 for i := range ws { 305 ws[i].Init() 306 br.SubscribeEvents(ws[i].Receiver(), 1) 307 } 308 309 b.ResetTimer() 310 for i := 0; i < b.N; i++ { 311 br.Broadcast(1) 312 for j := range ws { 313 if got, want := ws[j].Pending(), Set(1); got != want { 314 b.Fatalf("Receiver.Pending(): got %#x, wanted %#x", got, want) 315 } 316 ws[j].Ack(1) 317 } 318 } 319 }) 320 } 321 } 322 323 func BenchmarkMapBroadcastAck(b *testing.B) { 324 for _, n := range receiverCountsNonZero { 325 b.Run(fmt.Sprintf("%d", n), func(b *testing.B) { 326 var mu sync.Mutex 327 m := make(map[*Receiver]Set) 328 ws := make([]Waiter, n) 329 for i := range ws { 330 ws[i].Init() 331 m[ws[i].Receiver()] = Set(1) 332 } 333 334 b.ResetTimer() 335 for i := 0; i < b.N; i++ { 336 mu.Lock() 337 for r := range m { 338 r.Notify(1) 339 } 340 mu.Unlock() 341 for j := range ws { 342 if got, want := ws[j].Pending(), Set(1); got != want { 343 b.Fatalf("Receiver.Pending(): got %#x, wanted %#x", got, want) 344 } 345 ws[j].Ack(1) 346 } 347 } 348 }) 349 } 350 } 351 352 func BenchmarkQueueBroadcastAck(b *testing.B) { 353 for _, n := range receiverCountsNonZero { 354 b.Run(fmt.Sprintf("%d", n), func(b *testing.B) { 355 var q waiter.Queue 356 chs := make([]chan struct{}, n) 357 for i := range chs { 358 e, ch := waiter.NewChannelEntry(1) 359 q.EventRegister(&e) 360 chs[i] = ch 361 } 362 363 b.ResetTimer() 364 for i := 0; i < b.N; i++ { 365 q.Notify(1) 366 for _, ch := range chs { 367 select { 368 case <-ch: 369 default: 370 b.Fatalf("channel did not receive event") 371 } 372 } 373 } 374 }) 375 } 376 }