github.com/karlmcguire/casn@v0.0.0-20191208212146-d789ce994bba/casn_test.go (about) 1 package casn 2 3 import ( 4 "fmt" 5 "math/rand" 6 "sync" 7 "sync/atomic" 8 "testing" 9 "time" 10 ) 11 12 func TestRDCSSRead(t *testing.T) { 13 data := []uint64{1, 2} 14 if rdcssRead(&data[0]) != 1 || rdcssRead(&data[1]) != 2 { 15 t.Fatal("rdcssRead returning wrong values") 16 } 17 } 18 19 func TestGetRDCSSDescriptor(t *testing.T) { 20 data := []uint64{0, 0} 21 d := &rdcssDescriptor{ 22 a1: &data[0], 23 o1: 0, 24 a2: &data[1], 25 o2: 0, 26 n2: 1, 27 } 28 if d != getRDCSSDescriptor(d.ptr()) { 29 t.Fatal("error") 30 } 31 } 32 33 func TestRDCSS(t *testing.T) { 34 control := uint64(0) 35 data := uint64(0) 36 old := rdcss(&rdcssDescriptor{ 37 a1: &control, 38 o1: 0, 39 a2: &data, 40 o2: 0, 41 n2: 1, 42 }) 43 if old != 0 && data != 1 { 44 t.Fatal("RDCSS failed") 45 } 46 } 47 48 func BenchmarkRDCSS(b *testing.B) { 49 data := []uint64{0, 0} 50 b.SetBytes(1) 51 for n := uint64(0); n < uint64(b.N); n++ { 52 rdcss(&rdcssDescriptor{ 53 a1: &data[0], 54 o1: 0, 55 a2: &data[1], 56 o2: n + 0, 57 n2: n + 1, 58 }) 59 } 60 } 61 62 func BenchmarkRDCSSParallel(b *testing.B) { 63 data := []uint64{0, 0} 64 desc := make([]*rdcssDescriptor, b.N) 65 for i := uint64(0); i < uint64(b.N); i++ { 66 desc[i] = &rdcssDescriptor{ 67 a1: &data[0], 68 o1: 0, 69 a2: &data[1], 70 o2: i + 0, 71 n2: i + 1, 72 } 73 } 74 b.SetBytes(1) 75 b.RunParallel(func(pb *testing.PB) { 76 for n := uint64(0); pb.Next(); n++ { 77 rdcss(desc[n]) 78 } 79 }) 80 } 81 82 func TestCASN(t *testing.T) { 83 data := []uint64{0, 1, 2, 3} 84 if CASN([]Update{ 85 {&data[0], 0, 1}, 86 {&data[1], 1, 2}, 87 {&data[2], 2, 3}, 88 {&data[3], 3, 4}, 89 }) != true { 90 t.Fatal("CASN should be successful") 91 } 92 if data[0] != 1 || data[1] != 2 || data[2] != 3 || data[3] != 4 { 93 t.Fatal("CASN didn't swap values") 94 } 95 if CASN([]Update{ 96 {&data[0], 0, 1}, 97 {&data[1], 1, 2}, 98 {&data[2], 2, 3}, 99 {&data[3], 3, 4}, 100 }) != false { 101 t.Fatal("CASN should have failed") 102 } 103 if data[0] != 1 || data[1] != 2 || data[2] != 3 || data[3] != 4 { 104 t.Fatal("CASN shouldn't have swapped values") 105 } 106 } 107 108 func BenchmarkMutex(b *testing.B) { 109 data := []uint64{0, 1, 2, 3} 110 mu := &sync.Mutex{} 111 b.SetBytes(4) 112 for n := uint64(0); n < uint64(b.N); n++ { 113 mu.Lock() 114 data[0] = n + 1 115 data[1] = n + 2 116 data[2] = n + 3 117 data[3] = n + 4 118 mu.Unlock() 119 } 120 } 121 122 func BenchmarkMutexParallel(b *testing.B) { 123 data := []uint64{0, 1, 2, 3} 124 mu := &sync.Mutex{} 125 b.SetBytes(4) 126 b.RunParallel(func(pb *testing.PB) { 127 for n := uint64(0); pb.Next(); n++ { 128 mu.Lock() 129 data[0] = n + 1 130 data[1] = n + 2 131 data[2] = n + 3 132 data[3] = n + 4 133 mu.Unlock() 134 } 135 }) 136 } 137 138 func BenchmarkCASN(b *testing.B) { 139 data := []uint64{0, 1, 2, 3} 140 b.SetBytes(4) 141 for n := uint64(0); n < uint64(b.N); n++ { 142 CASN([]Update{ 143 {&data[0], n + 0, n + 1}, 144 {&data[1], n + 1, n + 2}, 145 {&data[2], n + 2, n + 3}, 146 {&data[3], n + 3, n + 4}, 147 }) 148 } 149 } 150 151 func BenchmarkCASNParallel(b *testing.B) { 152 data := []uint64{0, 1, 2, 3} 153 b.SetBytes(4) 154 b.RunParallel(func(pb *testing.PB) { 155 for n := uint64(0); pb.Next(); n++ { 156 CASN([]Update{ 157 {&data[0], n + 0, n + 1}, 158 {&data[1], n + 1, n + 2}, 159 {&data[2], n + 2, n + 3}, 160 {&data[3], n + 3, n + 4}, 161 }) 162 } 163 }) 164 } 165 166 func TestCas(t *testing.T) { 167 num := uint64(0) 168 if old := cas(&num, 0, 1); old != 0 { 169 t.Fatal("Cas didn't swap") 170 } 171 if old := cas(&num, 2, 0); old != 1 { 172 t.Fatal("Cas shouldn't have swapped") 173 } 174 } 175 176 func BenchmarkCAS(b *testing.B) { 177 data := uint64(0) 178 b.SetBytes(1) 179 for n := uint64(0); n < uint64(b.N); n++ { 180 atomic.CompareAndSwapUint64(&data, n, n+1) 181 } 182 } 183 184 func BenchmarkCASParallel(b *testing.B) { 185 data := uint64(0) 186 b.SetBytes(1) 187 b.RunParallel(func(pb *testing.PB) { 188 for n := uint64(0); pb.Next(); n++ { 189 atomic.CompareAndSwapUint64(&data, n, n+1) 190 } 191 }) 192 } 193 194 const ( 195 V = 1024 196 M = V - 1 197 N = 4 198 P = 8 199 ) 200 201 // BenchmarkEvaluation runs a benchmark most similar to the one found in the 202 // original paper in order to get an idea of how closely we've followed the 203 // correct implementation. 204 func BenchmarkEvaluationCASN(b *testing.B) { 205 data := make([]uint64, V) 206 ctrl := make([]chan struct{}, P) 207 for i := range ctrl { 208 ctrl[i] = make(chan struct{}) 209 } 210 for i := 0; i < P; i++ { 211 go func(a int) { 212 <-ctrl[a] 213 r := rand.Int() 214 for j := 0; j < V; j++ { 215 loc := []int{ 216 ((j + r) + ((V / P) * 0)) & M, 217 ((j + r) + ((V / P) * 1)) & M, 218 ((j + r) + ((V / P) * 2)) & M, 219 ((j + r) + ((V / P) * 3)) & M, 220 } 221 old := []uint64{ 222 CASNRead(&data[loc[0]]), 223 CASNRead(&data[loc[1]]), 224 CASNRead(&data[loc[2]]), 225 CASNRead(&data[loc[3]]), 226 } 227 CASN([]Update{ 228 {&data[loc[0]], old[0], old[0] + 1}, 229 {&data[loc[1]], old[1], old[1] + 1}, 230 {&data[loc[2]], old[2], old[2] + 1}, 231 {&data[loc[3]], old[3], old[3] + 1}, 232 }) 233 } 234 ctrl[a] <- struct{}{} 235 }(i) 236 } 237 start := time.Now() 238 for i := range ctrl { 239 ctrl[i] <- struct{}{} 240 } 241 for i := range ctrl { 242 <-ctrl[i] 243 } 244 duration := time.Since(start) 245 b.ReportMetric(float64(duration/(V*P)), "ns") 246 fmt.Println(data) 247 }