github.com/ice-blockchain/go/src@v0.0.0-20240403114104-1564d284e521/runtime/chan_test.go (about) 1 // Copyright 2009 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package runtime_test 6 7 import ( 8 "internal/testenv" 9 "math" 10 "runtime" 11 "sync" 12 "sync/atomic" 13 "testing" 14 "time" 15 ) 16 17 func TestChan(t *testing.T) { 18 defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(4)) 19 N := 200 20 if testing.Short() { 21 N = 20 22 } 23 for chanCap := 0; chanCap < N; chanCap++ { 24 { 25 // Ensure that receive from empty chan blocks. 26 c := make(chan int, chanCap) 27 recv1 := false 28 go func() { 29 _ = <-c 30 recv1 = true 31 }() 32 recv2 := false 33 go func() { 34 _, _ = <-c 35 recv2 = true 36 }() 37 time.Sleep(time.Millisecond) 38 if recv1 || recv2 { 39 t.Fatalf("chan[%d]: receive from empty chan", chanCap) 40 } 41 // Ensure that non-blocking receive does not block. 42 select { 43 case _ = <-c: 44 t.Fatalf("chan[%d]: receive from empty chan", chanCap) 45 default: 46 } 47 select { 48 case _, _ = <-c: 49 t.Fatalf("chan[%d]: receive from empty chan", chanCap) 50 default: 51 } 52 c <- 0 53 c <- 0 54 } 55 56 { 57 // Ensure that send to full chan blocks. 58 c := make(chan int, chanCap) 59 for i := 0; i < chanCap; i++ { 60 c <- i 61 } 62 sent := uint32(0) 63 go func() { 64 c <- 0 65 atomic.StoreUint32(&sent, 1) 66 }() 67 time.Sleep(time.Millisecond) 68 if atomic.LoadUint32(&sent) != 0 { 69 t.Fatalf("chan[%d]: send to full chan", chanCap) 70 } 71 // Ensure that non-blocking send does not block. 72 select { 73 case c <- 0: 74 t.Fatalf("chan[%d]: send to full chan", chanCap) 75 default: 76 } 77 <-c 78 } 79 80 { 81 // Ensure that we receive 0 from closed chan. 82 c := make(chan int, chanCap) 83 for i := 0; i < chanCap; i++ { 84 c <- i 85 } 86 close(c) 87 for i := 0; i < chanCap; i++ { 88 v := <-c 89 if v != i { 90 t.Fatalf("chan[%d]: received %v, expected %v", chanCap, v, i) 91 } 92 } 93 if v := <-c; v != 0 { 94 t.Fatalf("chan[%d]: received %v, expected %v", chanCap, v, 0) 95 } 96 if v, ok := <-c; v != 0 || ok { 97 t.Fatalf("chan[%d]: received %v/%v, expected %v/%v", chanCap, v, ok, 0, false) 98 } 99 } 100 101 { 102 // Ensure that close unblocks receive. 103 c := make(chan int, chanCap) 104 done := make(chan bool) 105 go func() { 106 v, ok := <-c 107 done <- v == 0 && ok == false 108 }() 109 time.Sleep(time.Millisecond) 110 close(c) 111 if !<-done { 112 t.Fatalf("chan[%d]: received non zero from closed chan", chanCap) 113 } 114 } 115 116 { 117 // Send 100 integers, 118 // ensure that we receive them non-corrupted in FIFO order. 119 c := make(chan int, chanCap) 120 go func() { 121 for i := 0; i < 100; i++ { 122 c <- i 123 } 124 }() 125 for i := 0; i < 100; i++ { 126 v := <-c 127 if v != i { 128 t.Fatalf("chan[%d]: received %v, expected %v", chanCap, v, i) 129 } 130 } 131 132 // Same, but using recv2. 133 go func() { 134 for i := 0; i < 100; i++ { 135 c <- i 136 } 137 }() 138 for i := 0; i < 100; i++ { 139 v, ok := <-c 140 if !ok { 141 t.Fatalf("chan[%d]: receive failed, expected %v", chanCap, i) 142 } 143 if v != i { 144 t.Fatalf("chan[%d]: received %v, expected %v", chanCap, v, i) 145 } 146 } 147 148 // Send 1000 integers in 4 goroutines, 149 // ensure that we receive what we send. 150 const P = 4 151 const L = 1000 152 for p := 0; p < P; p++ { 153 go func() { 154 for i := 0; i < L; i++ { 155 c <- i 156 } 157 }() 158 } 159 done := make(chan map[int]int) 160 for p := 0; p < P; p++ { 161 go func() { 162 recv := make(map[int]int) 163 for i := 0; i < L; i++ { 164 v := <-c 165 recv[v] = recv[v] + 1 166 } 167 done <- recv 168 }() 169 } 170 recv := make(map[int]int) 171 for p := 0; p < P; p++ { 172 for k, v := range <-done { 173 recv[k] = recv[k] + v 174 } 175 } 176 if len(recv) != L { 177 t.Fatalf("chan[%d]: received %v values, expected %v", chanCap, len(recv), L) 178 } 179 for _, v := range recv { 180 if v != P { 181 t.Fatalf("chan[%d]: received %v values, expected %v", chanCap, v, P) 182 } 183 } 184 } 185 186 { 187 // Test len/cap. 188 c := make(chan int, chanCap) 189 if len(c) != 0 || cap(c) != chanCap { 190 t.Fatalf("chan[%d]: bad len/cap, expect %v/%v, got %v/%v", chanCap, 0, chanCap, len(c), cap(c)) 191 } 192 for i := 0; i < chanCap; i++ { 193 c <- i 194 } 195 if len(c) != chanCap || cap(c) != chanCap { 196 t.Fatalf("chan[%d]: bad len/cap, expect %v/%v, got %v/%v", chanCap, chanCap, chanCap, len(c), cap(c)) 197 } 198 } 199 200 } 201 } 202 203 func TestNonblockRecvRace(t *testing.T) { 204 n := 10000 205 if testing.Short() { 206 n = 100 207 } 208 for i := 0; i < n; i++ { 209 c := make(chan int, 1) 210 c <- 1 211 go func() { 212 select { 213 case <-c: 214 default: 215 t.Error("chan is not ready") 216 } 217 }() 218 close(c) 219 <-c 220 if t.Failed() { 221 return 222 } 223 } 224 } 225 226 // This test checks that select acts on the state of the channels at one 227 // moment in the execution, not over a smeared time window. 228 // In the test, one goroutine does: 229 // 230 // create c1, c2 231 // make c1 ready for receiving 232 // create second goroutine 233 // make c2 ready for receiving 234 // make c1 no longer ready for receiving (if possible) 235 // 236 // The second goroutine does a non-blocking select receiving from c1 and c2. 237 // From the time the second goroutine is created, at least one of c1 and c2 238 // is always ready for receiving, so the select in the second goroutine must 239 // always receive from one or the other. It must never execute the default case. 240 func TestNonblockSelectRace(t *testing.T) { 241 n := 100000 242 if testing.Short() { 243 n = 1000 244 } 245 done := make(chan bool, 1) 246 for i := 0; i < n; i++ { 247 c1 := make(chan int, 1) 248 c2 := make(chan int, 1) 249 c1 <- 1 250 go func() { 251 select { 252 case <-c1: 253 case <-c2: 254 default: 255 done <- false 256 return 257 } 258 done <- true 259 }() 260 c2 <- 1 261 select { 262 case <-c1: 263 default: 264 } 265 if !<-done { 266 t.Fatal("no chan is ready") 267 } 268 } 269 } 270 271 // Same as TestNonblockSelectRace, but close(c2) replaces c2 <- 1. 272 func TestNonblockSelectRace2(t *testing.T) { 273 n := 100000 274 if testing.Short() { 275 n = 1000 276 } 277 done := make(chan bool, 1) 278 for i := 0; i < n; i++ { 279 c1 := make(chan int, 1) 280 c2 := make(chan int) 281 c1 <- 1 282 go func() { 283 select { 284 case <-c1: 285 case <-c2: 286 default: 287 done <- false 288 return 289 } 290 done <- true 291 }() 292 close(c2) 293 select { 294 case <-c1: 295 default: 296 } 297 if !<-done { 298 t.Fatal("no chan is ready") 299 } 300 } 301 } 302 303 func TestSelfSelect(t *testing.T) { 304 // Ensure that send/recv on the same chan in select 305 // does not crash nor deadlock. 306 defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(2)) 307 for _, chanCap := range []int{0, 10} { 308 var wg sync.WaitGroup 309 wg.Add(2) 310 c := make(chan int, chanCap) 311 for p := 0; p < 2; p++ { 312 p := p 313 go func() { 314 defer wg.Done() 315 for i := 0; i < 1000; i++ { 316 if p == 0 || i%2 == 0 { 317 select { 318 case c <- p: 319 case v := <-c: 320 if chanCap == 0 && v == p { 321 t.Errorf("self receive") 322 return 323 } 324 } 325 } else { 326 select { 327 case v := <-c: 328 if chanCap == 0 && v == p { 329 t.Errorf("self receive") 330 return 331 } 332 case c <- p: 333 } 334 } 335 } 336 }() 337 } 338 wg.Wait() 339 } 340 } 341 342 func TestSelectStress(t *testing.T) { 343 defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(10)) 344 var c [4]chan int 345 c[0] = make(chan int) 346 c[1] = make(chan int) 347 c[2] = make(chan int, 2) 348 c[3] = make(chan int, 3) 349 N := int(1e5) 350 if testing.Short() { 351 N /= 10 352 } 353 // There are 4 goroutines that send N values on each of the chans, 354 // + 4 goroutines that receive N values on each of the chans, 355 // + 1 goroutine that sends N values on each of the chans in a single select, 356 // + 1 goroutine that receives N values on each of the chans in a single select. 357 // All these sends, receives and selects interact chaotically at runtime, 358 // but we are careful that this whole construct does not deadlock. 359 var wg sync.WaitGroup 360 wg.Add(10) 361 for k := 0; k < 4; k++ { 362 k := k 363 go func() { 364 for i := 0; i < N; i++ { 365 c[k] <- 0 366 } 367 wg.Done() 368 }() 369 go func() { 370 for i := 0; i < N; i++ { 371 <-c[k] 372 } 373 wg.Done() 374 }() 375 } 376 go func() { 377 var n [4]int 378 c1 := c 379 for i := 0; i < 4*N; i++ { 380 select { 381 case c1[3] <- 0: 382 n[3]++ 383 if n[3] == N { 384 c1[3] = nil 385 } 386 case c1[2] <- 0: 387 n[2]++ 388 if n[2] == N { 389 c1[2] = nil 390 } 391 case c1[0] <- 0: 392 n[0]++ 393 if n[0] == N { 394 c1[0] = nil 395 } 396 case c1[1] <- 0: 397 n[1]++ 398 if n[1] == N { 399 c1[1] = nil 400 } 401 } 402 } 403 wg.Done() 404 }() 405 go func() { 406 var n [4]int 407 c1 := c 408 for i := 0; i < 4*N; i++ { 409 select { 410 case <-c1[0]: 411 n[0]++ 412 if n[0] == N { 413 c1[0] = nil 414 } 415 case <-c1[1]: 416 n[1]++ 417 if n[1] == N { 418 c1[1] = nil 419 } 420 case <-c1[2]: 421 n[2]++ 422 if n[2] == N { 423 c1[2] = nil 424 } 425 case <-c1[3]: 426 n[3]++ 427 if n[3] == N { 428 c1[3] = nil 429 } 430 } 431 } 432 wg.Done() 433 }() 434 wg.Wait() 435 } 436 437 func TestSelectFairness(t *testing.T) { 438 const trials = 10000 439 if runtime.GOOS == "linux" && runtime.GOARCH == "ppc64le" { 440 testenv.SkipFlaky(t, 22047) 441 } 442 c1 := make(chan byte, trials+1) 443 c2 := make(chan byte, trials+1) 444 for i := 0; i < trials+1; i++ { 445 c1 <- 1 446 c2 <- 2 447 } 448 c3 := make(chan byte) 449 c4 := make(chan byte) 450 out := make(chan byte) 451 done := make(chan byte) 452 var wg sync.WaitGroup 453 wg.Add(1) 454 go func() { 455 defer wg.Done() 456 for { 457 var b byte 458 select { 459 case b = <-c3: 460 case b = <-c4: 461 case b = <-c1: 462 case b = <-c2: 463 } 464 select { 465 case out <- b: 466 case <-done: 467 return 468 } 469 } 470 }() 471 cnt1, cnt2 := 0, 0 472 for i := 0; i < trials; i++ { 473 switch b := <-out; b { 474 case 1: 475 cnt1++ 476 case 2: 477 cnt2++ 478 default: 479 t.Fatalf("unexpected value %d on channel", b) 480 } 481 } 482 // If the select in the goroutine is fair, 483 // cnt1 and cnt2 should be about the same value. 484 // See if we're more than 10 sigma away from the expected value. 485 // 10 sigma is a lot, but we're ok with some systematic bias as 486 // long as it isn't too severe. 487 const mean = trials * 0.5 488 const variance = trials * 0.5 * (1 - 0.5) 489 stddev := math.Sqrt(variance) 490 if math.Abs(float64(cnt1-mean)) > 10*stddev { 491 t.Errorf("unfair select: in %d trials, results were %d, %d", trials, cnt1, cnt2) 492 } 493 close(done) 494 wg.Wait() 495 } 496 497 func TestChanSendInterface(t *testing.T) { 498 type mt struct{} 499 m := &mt{} 500 c := make(chan any, 1) 501 c <- m 502 select { 503 case c <- m: 504 default: 505 } 506 select { 507 case c <- m: 508 case c <- &mt{}: 509 default: 510 } 511 } 512 513 func TestPseudoRandomSend(t *testing.T) { 514 n := 100 515 for _, chanCap := range []int{0, n} { 516 c := make(chan int, chanCap) 517 l := make([]int, n) 518 var m sync.Mutex 519 m.Lock() 520 go func() { 521 for i := 0; i < n; i++ { 522 runtime.Gosched() 523 l[i] = <-c 524 } 525 m.Unlock() 526 }() 527 for i := 0; i < n; i++ { 528 select { 529 case c <- 1: 530 case c <- 0: 531 } 532 } 533 m.Lock() // wait 534 n0 := 0 535 n1 := 0 536 for _, i := range l { 537 n0 += (i + 1) % 2 538 n1 += i 539 } 540 if n0 <= n/10 || n1 <= n/10 { 541 t.Errorf("Want pseudorandom, got %d zeros and %d ones (chan cap %d)", n0, n1, chanCap) 542 } 543 } 544 } 545 546 func TestMultiConsumer(t *testing.T) { 547 const nwork = 23 548 const niter = 271828 549 550 pn := []int{2, 3, 7, 11, 13, 17, 19, 23, 27, 31} 551 552 q := make(chan int, nwork*3) 553 r := make(chan int, nwork*3) 554 555 // workers 556 var wg sync.WaitGroup 557 for i := 0; i < nwork; i++ { 558 wg.Add(1) 559 go func(w int) { 560 for v := range q { 561 // mess with the fifo-ish nature of range 562 if pn[w%len(pn)] == v { 563 runtime.Gosched() 564 } 565 r <- v 566 } 567 wg.Done() 568 }(i) 569 } 570 571 // feeder & closer 572 expect := 0 573 go func() { 574 for i := 0; i < niter; i++ { 575 v := pn[i%len(pn)] 576 expect += v 577 q <- v 578 } 579 close(q) // no more work 580 wg.Wait() // workers done 581 close(r) // ... so there can be no more results 582 }() 583 584 // consume & check 585 n := 0 586 s := 0 587 for v := range r { 588 n++ 589 s += v 590 } 591 if n != niter || s != expect { 592 t.Errorf("Expected sum %d (got %d) from %d iter (saw %d)", 593 expect, s, niter, n) 594 } 595 } 596 597 func TestShrinkStackDuringBlockedSend(t *testing.T) { 598 // make sure that channel operations still work when we are 599 // blocked on a channel send and we shrink the stack. 600 // NOTE: this test probably won't fail unless stack1.go:stackDebug 601 // is set to >= 1. 602 const n = 10 603 c := make(chan int) 604 done := make(chan struct{}) 605 606 go func() { 607 for i := 0; i < n; i++ { 608 c <- i 609 // use lots of stack, briefly. 610 stackGrowthRecursive(20) 611 } 612 done <- struct{}{} 613 }() 614 615 for i := 0; i < n; i++ { 616 x := <-c 617 if x != i { 618 t.Errorf("bad channel read: want %d, got %d", i, x) 619 } 620 // Waste some time so sender can finish using lots of stack 621 // and block in channel send. 622 time.Sleep(1 * time.Millisecond) 623 // trigger GC which will shrink the stack of the sender. 624 runtime.GC() 625 } 626 <-done 627 } 628 629 func TestNoShrinkStackWhileParking(t *testing.T) { 630 if runtime.GOOS == "netbsd" && runtime.GOARCH == "arm64" { 631 testenv.SkipFlaky(t, 49382) 632 } 633 if runtime.GOOS == "openbsd" { 634 testenv.SkipFlaky(t, 51482) 635 } 636 637 // The goal of this test is to trigger a "racy sudog adjustment" 638 // throw. Basically, there's a window between when a goroutine 639 // becomes available for preemption for stack scanning (and thus, 640 // stack shrinking) but before the goroutine has fully parked on a 641 // channel. See issue 40641 for more details on the problem. 642 // 643 // The way we try to induce this failure is to set up two 644 // goroutines: a sender and a receiver that communicate across 645 // a channel. We try to set up a situation where the sender 646 // grows its stack temporarily then *fully* blocks on a channel 647 // often. Meanwhile a GC is triggered so that we try to get a 648 // mark worker to shrink the sender's stack and race with the 649 // sender parking. 650 // 651 // Unfortunately the race window here is so small that we 652 // either need a ridiculous number of iterations, or we add 653 // "usleep(1000)" to park_m, just before the unlockf call. 654 const n = 10 655 send := func(c chan<- int, done chan struct{}) { 656 for i := 0; i < n; i++ { 657 c <- i 658 // Use lots of stack briefly so that 659 // the GC is going to want to shrink us 660 // when it scans us. Make sure not to 661 // do any function calls otherwise 662 // in order to avoid us shrinking ourselves 663 // when we're preempted. 664 stackGrowthRecursive(20) 665 } 666 done <- struct{}{} 667 } 668 recv := func(c <-chan int, done chan struct{}) { 669 for i := 0; i < n; i++ { 670 // Sleep here so that the sender always 671 // fully blocks. 672 time.Sleep(10 * time.Microsecond) 673 <-c 674 } 675 done <- struct{}{} 676 } 677 for i := 0; i < n*20; i++ { 678 c := make(chan int) 679 done := make(chan struct{}) 680 go recv(c, done) 681 go send(c, done) 682 // Wait a little bit before triggering 683 // the GC to make sure the sender and 684 // receiver have gotten into their groove. 685 time.Sleep(50 * time.Microsecond) 686 runtime.GC() 687 <-done 688 <-done 689 } 690 } 691 692 func TestSelectDuplicateChannel(t *testing.T) { 693 // This test makes sure we can queue a G on 694 // the same channel multiple times. 695 c := make(chan int) 696 d := make(chan int) 697 e := make(chan int) 698 699 // goroutine A 700 go func() { 701 select { 702 case <-c: 703 case <-c: 704 case <-d: 705 } 706 e <- 9 707 }() 708 time.Sleep(time.Millisecond) // make sure goroutine A gets queued first on c 709 710 // goroutine B 711 go func() { 712 <-c 713 }() 714 time.Sleep(time.Millisecond) // make sure goroutine B gets queued on c before continuing 715 716 d <- 7 // wake up A, it dequeues itself from c. This operation used to corrupt c.recvq. 717 <-e // A tells us it's done 718 c <- 8 // wake up B. This operation used to fail because c.recvq was corrupted (it tries to wake up an already running G instead of B) 719 } 720 721 func TestSelectStackAdjust(t *testing.T) { 722 // Test that channel receive slots that contain local stack 723 // pointers are adjusted correctly by stack shrinking. 724 c := make(chan *int) 725 d := make(chan *int) 726 ready1 := make(chan bool) 727 ready2 := make(chan bool) 728 729 f := func(ready chan bool, dup bool) { 730 // Temporarily grow the stack to 10K. 731 stackGrowthRecursive((10 << 10) / (128 * 8)) 732 733 // We're ready to trigger GC and stack shrink. 734 ready <- true 735 736 val := 42 737 var cx *int 738 cx = &val 739 740 var c2 chan *int 741 var d2 chan *int 742 if dup { 743 c2 = c 744 d2 = d 745 } 746 747 // Receive from d. cx won't be affected. 748 select { 749 case cx = <-c: 750 case <-c2: 751 case <-d: 752 case <-d2: 753 } 754 755 // Check that pointer in cx was adjusted correctly. 756 if cx != &val { 757 t.Error("cx no longer points to val") 758 } else if val != 42 { 759 t.Error("val changed") 760 } else { 761 *cx = 43 762 if val != 43 { 763 t.Error("changing *cx failed to change val") 764 } 765 } 766 ready <- true 767 } 768 769 go f(ready1, false) 770 go f(ready2, true) 771 772 // Let the goroutines get into the select. 773 <-ready1 774 <-ready2 775 time.Sleep(10 * time.Millisecond) 776 777 // Force concurrent GC to shrink the stacks. 778 runtime.GC() 779 780 // Wake selects. 781 close(d) 782 <-ready1 783 <-ready2 784 } 785 786 type struct0 struct{} 787 788 func BenchmarkMakeChan(b *testing.B) { 789 b.Run("Byte", func(b *testing.B) { 790 var x chan byte 791 for i := 0; i < b.N; i++ { 792 x = make(chan byte, 8) 793 } 794 close(x) 795 }) 796 b.Run("Int", func(b *testing.B) { 797 var x chan int 798 for i := 0; i < b.N; i++ { 799 x = make(chan int, 8) 800 } 801 close(x) 802 }) 803 b.Run("Ptr", func(b *testing.B) { 804 var x chan *byte 805 for i := 0; i < b.N; i++ { 806 x = make(chan *byte, 8) 807 } 808 close(x) 809 }) 810 b.Run("Struct", func(b *testing.B) { 811 b.Run("0", func(b *testing.B) { 812 var x chan struct0 813 for i := 0; i < b.N; i++ { 814 x = make(chan struct0, 8) 815 } 816 close(x) 817 }) 818 b.Run("32", func(b *testing.B) { 819 var x chan struct32 820 for i := 0; i < b.N; i++ { 821 x = make(chan struct32, 8) 822 } 823 close(x) 824 }) 825 b.Run("40", func(b *testing.B) { 826 var x chan struct40 827 for i := 0; i < b.N; i++ { 828 x = make(chan struct40, 8) 829 } 830 close(x) 831 }) 832 }) 833 } 834 835 func BenchmarkChanNonblocking(b *testing.B) { 836 myc := make(chan int) 837 b.RunParallel(func(pb *testing.PB) { 838 for pb.Next() { 839 select { 840 case <-myc: 841 default: 842 } 843 } 844 }) 845 } 846 847 func BenchmarkSelectUncontended(b *testing.B) { 848 b.RunParallel(func(pb *testing.PB) { 849 myc1 := make(chan int, 1) 850 myc2 := make(chan int, 1) 851 myc1 <- 0 852 for pb.Next() { 853 select { 854 case <-myc1: 855 myc2 <- 0 856 case <-myc2: 857 myc1 <- 0 858 } 859 } 860 }) 861 } 862 863 func BenchmarkSelectSyncContended(b *testing.B) { 864 myc1 := make(chan int) 865 myc2 := make(chan int) 866 myc3 := make(chan int) 867 done := make(chan int) 868 b.RunParallel(func(pb *testing.PB) { 869 go func() { 870 for { 871 select { 872 case myc1 <- 0: 873 case myc2 <- 0: 874 case myc3 <- 0: 875 case <-done: 876 return 877 } 878 } 879 }() 880 for pb.Next() { 881 select { 882 case <-myc1: 883 case <-myc2: 884 case <-myc3: 885 } 886 } 887 }) 888 close(done) 889 } 890 891 func BenchmarkSelectAsyncContended(b *testing.B) { 892 procs := runtime.GOMAXPROCS(0) 893 myc1 := make(chan int, procs) 894 myc2 := make(chan int, procs) 895 b.RunParallel(func(pb *testing.PB) { 896 myc1 <- 0 897 for pb.Next() { 898 select { 899 case <-myc1: 900 myc2 <- 0 901 case <-myc2: 902 myc1 <- 0 903 } 904 } 905 }) 906 } 907 908 func BenchmarkSelectNonblock(b *testing.B) { 909 myc1 := make(chan int) 910 myc2 := make(chan int) 911 myc3 := make(chan int, 1) 912 myc4 := make(chan int, 1) 913 b.RunParallel(func(pb *testing.PB) { 914 for pb.Next() { 915 select { 916 case <-myc1: 917 default: 918 } 919 select { 920 case myc2 <- 0: 921 default: 922 } 923 select { 924 case <-myc3: 925 default: 926 } 927 select { 928 case myc4 <- 0: 929 default: 930 } 931 } 932 }) 933 } 934 935 func BenchmarkChanUncontended(b *testing.B) { 936 const C = 100 937 b.RunParallel(func(pb *testing.PB) { 938 myc := make(chan int, C) 939 for pb.Next() { 940 for i := 0; i < C; i++ { 941 myc <- 0 942 } 943 for i := 0; i < C; i++ { 944 <-myc 945 } 946 } 947 }) 948 } 949 950 func BenchmarkChanContended(b *testing.B) { 951 const C = 100 952 myc := make(chan int, C*runtime.GOMAXPROCS(0)) 953 b.RunParallel(func(pb *testing.PB) { 954 for pb.Next() { 955 for i := 0; i < C; i++ { 956 myc <- 0 957 } 958 for i := 0; i < C; i++ { 959 <-myc 960 } 961 } 962 }) 963 } 964 965 func benchmarkChanSync(b *testing.B, work int) { 966 const CallsPerSched = 1000 967 procs := 2 968 N := int32(b.N / CallsPerSched / procs * procs) 969 c := make(chan bool, procs) 970 myc := make(chan int) 971 for p := 0; p < procs; p++ { 972 go func() { 973 for { 974 i := atomic.AddInt32(&N, -1) 975 if i < 0 { 976 break 977 } 978 for g := 0; g < CallsPerSched; g++ { 979 if i%2 == 0 { 980 <-myc 981 localWork(work) 982 myc <- 0 983 localWork(work) 984 } else { 985 myc <- 0 986 localWork(work) 987 <-myc 988 localWork(work) 989 } 990 } 991 } 992 c <- true 993 }() 994 } 995 for p := 0; p < procs; p++ { 996 <-c 997 } 998 } 999 1000 func BenchmarkChanSync(b *testing.B) { 1001 benchmarkChanSync(b, 0) 1002 } 1003 1004 func BenchmarkChanSyncWork(b *testing.B) { 1005 benchmarkChanSync(b, 1000) 1006 } 1007 1008 func benchmarkChanProdCons(b *testing.B, chanSize, localWork int) { 1009 const CallsPerSched = 1000 1010 procs := runtime.GOMAXPROCS(-1) 1011 N := int32(b.N / CallsPerSched) 1012 c := make(chan bool, 2*procs) 1013 myc := make(chan int, chanSize) 1014 for p := 0; p < procs; p++ { 1015 go func() { 1016 foo := 0 1017 for atomic.AddInt32(&N, -1) >= 0 { 1018 for g := 0; g < CallsPerSched; g++ { 1019 for i := 0; i < localWork; i++ { 1020 foo *= 2 1021 foo /= 2 1022 } 1023 myc <- 1 1024 } 1025 } 1026 myc <- 0 1027 c <- foo == 42 1028 }() 1029 go func() { 1030 foo := 0 1031 for { 1032 v := <-myc 1033 if v == 0 { 1034 break 1035 } 1036 for i := 0; i < localWork; i++ { 1037 foo *= 2 1038 foo /= 2 1039 } 1040 } 1041 c <- foo == 42 1042 }() 1043 } 1044 for p := 0; p < procs; p++ { 1045 <-c 1046 <-c 1047 } 1048 } 1049 1050 func BenchmarkChanProdCons0(b *testing.B) { 1051 benchmarkChanProdCons(b, 0, 0) 1052 } 1053 1054 func BenchmarkChanProdCons10(b *testing.B) { 1055 benchmarkChanProdCons(b, 10, 0) 1056 } 1057 1058 func BenchmarkChanProdCons100(b *testing.B) { 1059 benchmarkChanProdCons(b, 100, 0) 1060 } 1061 1062 func BenchmarkChanProdConsWork0(b *testing.B) { 1063 benchmarkChanProdCons(b, 0, 100) 1064 } 1065 1066 func BenchmarkChanProdConsWork10(b *testing.B) { 1067 benchmarkChanProdCons(b, 10, 100) 1068 } 1069 1070 func BenchmarkChanProdConsWork100(b *testing.B) { 1071 benchmarkChanProdCons(b, 100, 100) 1072 } 1073 1074 func BenchmarkSelectProdCons(b *testing.B) { 1075 const CallsPerSched = 1000 1076 procs := runtime.GOMAXPROCS(-1) 1077 N := int32(b.N / CallsPerSched) 1078 c := make(chan bool, 2*procs) 1079 myc := make(chan int, 128) 1080 myclose := make(chan bool) 1081 for p := 0; p < procs; p++ { 1082 go func() { 1083 // Producer: sends to myc. 1084 foo := 0 1085 // Intended to not fire during benchmarking. 1086 mytimer := time.After(time.Hour) 1087 for atomic.AddInt32(&N, -1) >= 0 { 1088 for g := 0; g < CallsPerSched; g++ { 1089 // Model some local work. 1090 for i := 0; i < 100; i++ { 1091 foo *= 2 1092 foo /= 2 1093 } 1094 select { 1095 case myc <- 1: 1096 case <-mytimer: 1097 case <-myclose: 1098 } 1099 } 1100 } 1101 myc <- 0 1102 c <- foo == 42 1103 }() 1104 go func() { 1105 // Consumer: receives from myc. 1106 foo := 0 1107 // Intended to not fire during benchmarking. 1108 mytimer := time.After(time.Hour) 1109 loop: 1110 for { 1111 select { 1112 case v := <-myc: 1113 if v == 0 { 1114 break loop 1115 } 1116 case <-mytimer: 1117 case <-myclose: 1118 } 1119 // Model some local work. 1120 for i := 0; i < 100; i++ { 1121 foo *= 2 1122 foo /= 2 1123 } 1124 } 1125 c <- foo == 42 1126 }() 1127 } 1128 for p := 0; p < procs; p++ { 1129 <-c 1130 <-c 1131 } 1132 } 1133 1134 func BenchmarkReceiveDataFromClosedChan(b *testing.B) { 1135 count := b.N 1136 ch := make(chan struct{}, count) 1137 for i := 0; i < count; i++ { 1138 ch <- struct{}{} 1139 } 1140 close(ch) 1141 1142 b.ResetTimer() 1143 for range ch { 1144 } 1145 } 1146 1147 func BenchmarkChanCreation(b *testing.B) { 1148 b.RunParallel(func(pb *testing.PB) { 1149 for pb.Next() { 1150 myc := make(chan int, 1) 1151 myc <- 0 1152 <-myc 1153 } 1154 }) 1155 } 1156 1157 func BenchmarkChanSem(b *testing.B) { 1158 type Empty struct{} 1159 myc := make(chan Empty, runtime.GOMAXPROCS(0)) 1160 b.RunParallel(func(pb *testing.PB) { 1161 for pb.Next() { 1162 myc <- Empty{} 1163 <-myc 1164 } 1165 }) 1166 } 1167 1168 func BenchmarkChanPopular(b *testing.B) { 1169 const n = 1000 1170 c := make(chan bool) 1171 var a []chan bool 1172 var wg sync.WaitGroup 1173 wg.Add(n) 1174 for j := 0; j < n; j++ { 1175 d := make(chan bool) 1176 a = append(a, d) 1177 go func() { 1178 for i := 0; i < b.N; i++ { 1179 select { 1180 case <-c: 1181 case <-d: 1182 } 1183 } 1184 wg.Done() 1185 }() 1186 } 1187 for i := 0; i < b.N; i++ { 1188 for _, d := range a { 1189 d <- true 1190 } 1191 } 1192 wg.Wait() 1193 } 1194 1195 func BenchmarkChanClosed(b *testing.B) { 1196 c := make(chan struct{}) 1197 close(c) 1198 b.RunParallel(func(pb *testing.PB) { 1199 for pb.Next() { 1200 select { 1201 case <-c: 1202 default: 1203 b.Error("Unreachable") 1204 } 1205 } 1206 }) 1207 } 1208 1209 var ( 1210 alwaysFalse = false 1211 workSink = 0 1212 ) 1213 1214 func localWork(w int) { 1215 foo := 0 1216 for i := 0; i < w; i++ { 1217 foo /= (foo + 1) 1218 } 1219 if alwaysFalse { 1220 workSink += foo 1221 } 1222 }