summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--pkg/sleep/sleep_test.go32
-rw-r--r--pkg/syncevent/waiter_test.go205
2 files changed, 158 insertions, 79 deletions
diff --git a/pkg/sleep/sleep_test.go b/pkg/sleep/sleep_test.go
index b27feb99b..f4af9d710 100644
--- a/pkg/sleep/sleep_test.go
+++ b/pkg/sleep/sleep_test.go
@@ -489,9 +489,7 @@ func BenchmarkGoAssertNonWaiting(b *testing.B) {
}
// BenchmarkSleeperWaitOnSingleSelect measures how long it takes to wait on one
-// waker channel while another goroutine wakes up the sleeper. This assumes that
-// a new goroutine doesn't run immediately (i.e., the creator of a new goroutine
-// is allowed to go to sleep before the new goroutine has a chance to run).
+// waker channel while another goroutine wakes up the sleeper.
func BenchmarkSleeperWaitOnSingleSelect(b *testing.B) {
var (
s Sleeper
@@ -514,25 +512,24 @@ func BenchmarkSleeperWaitOnSingleSelect(b *testing.B) {
}
// BenchmarkGoWaitOnSingleSelect measures how long it takes to wait on one
-// channel while another goroutine wakes up the sleeper. This assumes that a new
-// goroutine doesn't run immediately (i.e., the creator of a new goroutine is
-// allowed to go to sleep before the new goroutine has a chance to run).
+// channel while another goroutine wakes up the sleeper.
func BenchmarkGoWaitOnSingleSelect(b *testing.B) {
- ch := make(chan struct{})
+ ch := make(chan struct{}, 1)
+ nch := make(chan struct{}, 1)
go func() {
for i := 0; i < b.N; i++ {
+ <-nch
ch <- struct{}{}
}
}()
for i := 0; i < b.N; i++ {
+ nch <- struct{}{}
<-ch
}
}
// BenchmarkSleeperWaitOnMultiSelect measures how long it takes to wait on 4
-// wakers while another goroutine wakes up the sleeper. This assumes that a new
-// goroutine doesn't run immediately (i.e., the creator of a new goroutine is
-// allowed to go to sleep before the new goroutine has a chance to run).
+// wakers while another goroutine wakes up the sleeper.
func BenchmarkSleeperWaitOnMultiSelect(b *testing.B) {
const count = 4
var (
@@ -560,23 +557,30 @@ func BenchmarkSleeperWaitOnMultiSelect(b *testing.B) {
}
// BenchmarkGoWaitOnMultiSelect measures how long it takes to wait on 4 channels
-// while another goroutine wakes up the sleeper. This assumes that a new
-// goroutine doesn't run immediately (i.e., the creator of a new goroutine is
-// allowed to go to sleep before the new goroutine has a chance to run).
+// while another goroutine wakes up the sleeper.
func BenchmarkGoWaitOnMultiSelect(b *testing.B) {
const count = 4
ch := make([]chan struct{}, count)
+ nch := make([]chan struct{}, count)
for i := range ch {
- ch[i] = make(chan struct{})
+ ch[i] = make(chan struct{}, 1)
+ nch[i] = make(chan struct{}, 1)
}
b.ResetTimer()
go func() {
for i := 0; i < b.N; i++ {
+ select {
+ case <-nch[0]:
+ case <-nch[1]:
+ case <-nch[2]:
+ case <-nch[3]:
+ }
ch[count-1] <- struct{}{}
}
}()
for i := 0; i < b.N; i++ {
+ nch[count-1] <- struct{}{}
select {
case <-ch[0]:
case <-ch[1]:
diff --git a/pkg/syncevent/waiter_test.go b/pkg/syncevent/waiter_test.go
index cfa0972c0..428b20d0d 100644
--- a/pkg/syncevent/waiter_test.go
+++ b/pkg/syncevent/waiter_test.go
@@ -15,6 +15,7 @@
package syncevent
import (
+ "fmt"
"sync/atomic"
"testing"
"time"
@@ -303,112 +304,186 @@ func BenchmarkChannelNotifyWaitMultiAck(b *testing.B) {
}
}
-// BenchmarkXxxNotifyAsyncWaitAck measures how long it takes to wait for an
-// event while another goroutine signals the event. This assumes that a new
-// goroutine doesn't run immediately (i.e. the creator of a new goroutine is
-// allowed to go to sleep before the new goroutine has a chance to run).
+// BenchmarkXxxPingPong exchanges control between two goroutines.
-func BenchmarkWaiterNotifyAsyncWaitAck(b *testing.B) {
- var w Waiter
- w.Init()
+func BenchmarkWaiterPingPong(b *testing.B) {
+ var w1, w2 Waiter
+ w1.Init()
+ w2.Init()
+ var wg sync.WaitGroup
+ defer wg.Wait()
+ w1.Notify(evBench)
b.ResetTimer()
+ go func() {
+ for i := 0; i < b.N; i++ {
+ w1.Wait()
+ w1.Ack(evBench)
+ w2.Notify(evBench)
+ }
+ }()
for i := 0; i < b.N; i++ {
- go func() {
- w.Notify(1)
- }()
- w.Wait()
- w.Ack(evBench)
+ w2.Wait()
+ w2.Ack(evBench)
+ w1.Notify(evBench)
}
}
-func BenchmarkSleeperNotifyAsyncWaitAck(b *testing.B) {
- var s sleep.Sleeper
- var w sleep.Waker
- s.AddWaker(&w)
-
+func BenchmarkSleeperPingPong(b *testing.B) {
+ var (
+ s1 sleep.Sleeper
+ w1 sleep.Waker
+ s2 sleep.Sleeper
+ w2 sleep.Waker
+ )
+ s1.AddWaker(&w1)
+ s2.AddWaker(&w2)
+ var wg sync.WaitGroup
+ defer wg.Wait()
+
+ w1.Assert()
+ wg.Add(1)
b.ResetTimer()
+ go func() {
+ defer wg.Done()
+ for i := 0; i < b.N; i++ {
+ s1.Fetch(true)
+ w2.Assert()
+ }
+ }()
for i := 0; i < b.N; i++ {
- go func() {
- w.Assert()
- }()
- s.Fetch(true)
+ s2.Fetch(true)
+ w1.Assert()
}
}
-func BenchmarkChannelNotifyAsyncWaitAck(b *testing.B) {
- ch := make(chan struct{}, 1)
+func BenchmarkChannelPingPong(b *testing.B) {
+ ch1 := make(chan struct{}, 1)
+ ch2 := make(chan struct{}, 1)
+ var wg sync.WaitGroup
+ defer wg.Wait()
+ ch1 <- struct{}{}
+ wg.Add(1)
b.ResetTimer()
+ go func() {
+ defer wg.Done()
+ for i := 0; i < b.N; i++ {
+ <-ch1
+ ch2 <- struct{}{}
+ }
+ }()
for i := 0; i < b.N; i++ {
- go func() {
- select {
- case ch <- struct{}{}:
- default:
- }
- }()
- <-ch
+ <-ch2
+ ch1 <- struct{}{}
}
}
-// BenchmarkXxxNotifyAsyncWaitMultiAck is equivalent to NotifyAsyncWaitAck, but
-// allows for multiple event sources.
+// BenchmarkXxxPingPongMulti is equivalent to PingPong, but allows each
+// goroutine to receive from multiple event sources (although only one is ever
+// signaled).
-func BenchmarkWaiterNotifyAsyncWaitMultiAck(b *testing.B) {
- var w Waiter
- w.Init()
+func BenchmarkWaiterPingPongMulti(b *testing.B) {
+ var w1, w2 Waiter
+ w1.Init()
+ w2.Init()
+ var wg sync.WaitGroup
+ defer wg.Wait()
+ w1.Notify(evBench)
+ wg.Add(1)
b.ResetTimer()
+ go func() {
+ defer wg.Done()
+ for i := 0; i < b.N; i++ {
+ if e := w1.Wait(); e != evBench {
+ // b.Fatalf() can only be called from the main goroutine.
+ panic(fmt.Sprintf("Wait: got %#x, wanted %#x", e, evBench))
+ }
+ w1.Ack(evBench)
+ w2.Notify(evBench)
+ }
+ }()
for i := 0; i < b.N; i++ {
- go func() {
- w.Notify(evBench)
- }()
- if e := w.Wait(); e != evBench {
+ if e := w2.Wait(); e != evBench {
b.Fatalf("Wait: got %#x, wanted %#x", e, evBench)
}
- w.Ack(evBench)
+ w2.Ack(evBench)
+ w1.Notify(evBench)
}
}
-func BenchmarkSleeperNotifyAsyncWaitMultiAck(b *testing.B) {
- var s sleep.Sleeper
- var ws [3]sleep.Waker
- for i := range ws {
- s.AddWaker(&ws[i])
- }
-
+func BenchmarkSleeperPingPongMulti(b *testing.B) {
+ var (
+ s1 sleep.Sleeper
+ w1, w1a, w1b sleep.Waker
+ s2 sleep.Sleeper
+ w2, w2a, w2b sleep.Waker
+ )
+ s1.AddWaker(&w1)
+ s1.AddWaker(&w1a)
+ s1.AddWaker(&w1b)
+ s2.AddWaker(&w2)
+ s2.AddWaker(&w2a)
+ s2.AddWaker(&w2b)
+ var wg sync.WaitGroup
+ defer wg.Wait()
+
+ w1.Assert()
+ wg.Add(1)
b.ResetTimer()
+ go func() {
+ defer wg.Done()
+ for i := 0; i < b.N; i++ {
+ if w := s1.Fetch(true); w != &w1 {
+ // b.Fatalf() can only be called from the main goroutine.
+ panic(fmt.Sprintf("Fetch: got %p, wanted %p", w, &w1))
+ }
+ w2.Assert()
+ }
+ }()
for i := 0; i < b.N; i++ {
- go func() {
- ws[0].Assert()
- }()
- if v := s.Fetch(true); v != &ws[0] {
- b.Fatalf("Fetch: got %v, expected %v", v, &ws[0])
+ if w := s2.Fetch(true); w != &w2 {
+ b.Fatalf("Fetch: got %p, wanted %p", w, &w2)
}
+ w1.Assert()
}
}
-func BenchmarkChannelNotifyAsyncWaitMultiAck(b *testing.B) {
- ch0 := make(chan struct{}, 1)
+func BenchmarkChannelPingPongMulti(b *testing.B) {
ch1 := make(chan struct{}, 1)
+ ch1a := make(chan struct{}, 1)
+ ch1b := make(chan struct{}, 1)
ch2 := make(chan struct{}, 1)
+ ch2a := make(chan struct{}, 1)
+ ch2b := make(chan struct{}, 1)
+ var wg sync.WaitGroup
+ defer wg.Wait()
+ ch1 <- struct{}{}
+ wg.Add(1)
b.ResetTimer()
- for i := 0; i < b.N; i++ {
- go func() {
+ go func() {
+ defer wg.Done()
+ for i := 0; i < b.N; i++ {
select {
- case ch0 <- struct{}{}:
- default:
+ case <-ch1:
+ case <-ch1a:
+ panic("received from ch1a")
+ case <-ch1b:
+ panic("received from ch1a")
}
- }()
-
+ ch2 <- struct{}{}
+ }
+ }()
+ for i := 0; i < b.N; i++ {
select {
- case <-ch0:
- // ok
- case <-ch1:
- b.Fatalf("received from ch1")
case <-ch2:
- b.Fatalf("received from ch2")
+ case <-ch2a:
+ panic("received from ch2a")
+ case <-ch2b:
+ panic("received from ch2a")
}
+ ch1 <- struct{}{}
}
}