github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/kv/kvserver/spanlatch/signal_test.go (about) 1 // Copyright 2018 The Cockroach Authors. 2 // 3 // Use of this software is governed by the Business Source License 4 // included in the file licenses/BSL.txt. 5 // 6 // As of the Change Date specified in that file, in accordance with 7 // the Business Source License, use of this software will be governed 8 // by the Apache License, Version 2.0, included in the file 9 // licenses/APL.txt. 10 11 package spanlatch 12 13 import ( 14 "sync" 15 "sync/atomic" 16 "testing" 17 18 "github.com/stretchr/testify/require" 19 ) 20 21 func TestSignal(t *testing.T) { 22 var s signal 23 require.False(t, s.signaled()) 24 25 s.signal() 26 require.True(t, s.signaled()) 27 require.Equal(t, struct{}{}, <-s.signalChan()) 28 } 29 30 func TestSignalConcurrency(t *testing.T) { 31 const trials = 100 32 for i := 0; i < trials; i++ { 33 var s signal 34 var wg sync.WaitGroup 35 wg.Add(3) 36 go func() { 37 defer wg.Done() 38 <-s.signalChan() 39 require.True(t, s.signaled()) 40 }() 41 go func() { 42 defer wg.Done() 43 require.False(t, s.signaled()) 44 s.signal() 45 require.True(t, s.signaled()) 46 }() 47 go func() { 48 defer wg.Done() 49 <-s.signalChan() 50 require.True(t, s.signaled()) 51 }() 52 wg.Wait() 53 require.True(t, s.signaled()) 54 } 55 } 56 57 func BenchmarkSignaled(b *testing.B) { 58 var s signal 59 s.signal() 60 b.ResetTimer() 61 for i := 0; i < b.N; i++ { 62 _ = s.signaled() 63 } 64 } 65 66 func BenchmarkSignalBeforeChan(b *testing.B) { 67 var s signal 68 for i := 0; i < b.N; i++ { 69 s = signal{} // reset 70 s.signal() 71 } 72 } 73 74 func BenchmarkSignalAfterChan(b *testing.B) { 75 var s signal 76 chans := make([]chan struct{}, b.N) 77 for i := range chans { 78 chans[i] = make(chan struct{}) 79 } 80 b.ResetTimer() 81 for i := 0; i < b.N; i++ { 82 s = signal{} // reset 83 s.c = chanToPtr(chans[i]) 84 s.signal() 85 } 86 } 87 88 func BenchmarkInitialChanBeforeSignal(b *testing.B) { 89 var s signal 90 for i := 0; i < b.N; i++ { 91 s = signal{} // reset 92 _ = s.signalChan() 93 } 94 } 95 96 func BenchmarkSecondChanBeforeSignal(b *testing.B) { 97 var s signal 98 _ = s.signalChan() 99 b.ResetTimer() 100 for i := 0; i < b.N; i++ { 101 _ = s.signalChan() 102 } 103 } 104 105 func BenchmarkInitialChanAfterSignal(b *testing.B) { 106 var s signal 107 s.signal() 108 b.ResetTimer() 109 for i := 0; i < b.N; i++ { 110 s.c = nil 111 _ = s.signalChan() 112 } 113 } 114 115 func BenchmarkSecondChanAfterSignal(b *testing.B) { 116 var s signal 117 s.signal() 118 _ = s.signalChan() 119 b.ResetTimer() 120 for i := 0; i < b.N; i++ { 121 _ = s.signalChan() 122 } 123 } 124 125 // The following is a series of benchmarks demonstrating the value of the signal 126 // type and the fast-path that it provides. Closing channels to signal 127 // completion of a task is convenient, but in performance critical code paths it 128 // is essential to have a way to efficiently check for completion before falling 129 // back to waiting for the channel to close and entering select blocks. The 130 // benchmarks demonstrate that a channel on its own cannot be used to perform an 131 // efficient completion check, which is why the signal type mixes channels with 132 // atomics. The reason for this is that channels are forced to acquire an 133 // internal mutex before determining that they are closed and can return a zero 134 // value. This will always be more expensive than a single atomic load. 135 // 136 // Results with go1.10.4 on a Mac with a 3.1 GHz Intel Core i7 processor: 137 // 138 // ReadClosedChan-4 24.2ns ± 3% 139 // SingleSelectClosedChan-4 24.9ns ± 2% 140 // DefaultSelectClosedChan-4 24.6ns ± 1% 141 // MultiSelectClosedChan-4 97.9ns ± 2% 142 // Signaled-4 0.35ns ±13% 143 // 144 145 func BenchmarkReadClosedChan(b *testing.B) { 146 c := make(chan struct{}) 147 close(c) 148 for i := 0; i < b.N; i++ { 149 <-c 150 } 151 } 152 153 func BenchmarkSingleSelectClosedChan(b *testing.B) { 154 c := make(chan struct{}) 155 close(c) 156 //lint:ignore S1000 we don't want this simplified 157 for i := 0; i < b.N; i++ { 158 select { 159 case <-c: 160 } 161 } 162 } 163 164 func BenchmarkDefaultSelectClosedChan(b *testing.B) { 165 c := make(chan struct{}) 166 close(c) 167 for i := 0; i < b.N; i++ { 168 select { 169 case <-c: 170 default: 171 } 172 } 173 } 174 175 func BenchmarkMultiSelectClosedChan(b *testing.B) { 176 c, c2 := make(chan struct{}), make(chan struct{}) 177 close(c) 178 for i := 0; i < b.N; i++ { 179 select { 180 case <-c: 181 case <-c2: 182 } 183 } 184 } 185 186 func BenchmarkAtomicLoad(b *testing.B) { 187 a := int32(1) 188 for i := 0; i < b.N; i++ { 189 _ = atomic.LoadInt32(&a) 190 } 191 }