stress_test.go raw

   1  // Copyright (c) 2016 Uber Technologies, Inc.
   2  //
   3  // Permission is hereby granted, free of charge, to any person obtaining a copy
   4  // of this software and associated documentation files (the "Software"), to deal
   5  // in the Software without restriction, including without limitation the rights
   6  // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
   7  // copies of the Software, and to permit persons to whom the Software is
   8  // furnished to do so, subject to the following conditions:
   9  //
  10  // The above copyright notice and this permission notice shall be included in
  11  // all copies or substantial portions of the Software.
  12  //
  13  // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  14  // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  15  // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  16  // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  17  // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  18  // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  19  // THE SOFTWARE.
  20  
  21  package atomic
  22  
  23  import (
  24  	"errors"
  25  	"math"
  26  	"runtime"
  27  	"sync"
  28  	"sync/atomic"
  29  	"testing"
  30  	"time"
  31  )
  32  
  33  const (
  34  	_parallelism = 4
  35  	_iterations  = 1000
  36  )
  37  
  38  var _stressTests = map[string]func() func(){
  39  	"i32/std":  stressStdInt32,
  40  	"i32":      stressInt32,
  41  	"i64/std":  stressStdInt64,
  42  	"i64":      stressInt64,
  43  	"u32/std":  stressStdUint32,
  44  	"u32":      stressUint32,
  45  	"u64/std":  stressStdUint64,
  46  	"u64":      stressUint64,
  47  	"f64":      stressFloat64,
  48  	"bool":     stressBool,
  49  	"string":   stressString,
  50  	"duration": stressDuration,
  51  	"error":    stressError,
  52  	"time":     stressTime,
  53  }
  54  
  55  func TestStress(t *testing.T) {
  56  	for name, ff := range _stressTests {
  57  		t.Run(name, func(t *testing.T) {
  58  			defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(_parallelism))
  59  
  60  			start := make(chan struct{})
  61  			var wg sync.WaitGroup
  62  			wg.Add(_parallelism)
  63  			f := ff()
  64  			for i := 0; i < _parallelism; i++ {
  65  				go func() {
  66  					defer wg.Done()
  67  					<-start
  68  					for j := 0; j < _iterations; j++ {
  69  						f()
  70  					}
  71  				}()
  72  			}
  73  			close(start)
  74  			wg.Wait()
  75  		})
  76  	}
  77  }
  78  
  79  func BenchmarkStress(b *testing.B) {
  80  	for name, ff := range _stressTests {
  81  		b.Run(name, func(b *testing.B) {
  82  			f := ff()
  83  
  84  			b.Run("serial", func(b *testing.B) {
  85  				for i := 0; i < b.N; i++ {
  86  					f()
  87  				}
  88  			})
  89  
  90  			b.Run("parallel", func(b *testing.B) {
  91  				b.RunParallel(func(pb *testing.PB) {
  92  					for pb.Next() {
  93  						f()
  94  					}
  95  				})
  96  			})
  97  		})
  98  	}
  99  }
 100  
 101  func stressStdInt32() func() {
 102  	var atom int32
 103  	return func() {
 104  		atomic.LoadInt32(&atom)
 105  		atomic.AddInt32(&atom, 1)
 106  		atomic.AddInt32(&atom, -2)
 107  		atomic.AddInt32(&atom, 1)
 108  		atomic.AddInt32(&atom, -1)
 109  		atomic.CompareAndSwapInt32(&atom, 1, 0)
 110  		atomic.SwapInt32(&atom, 5)
 111  		atomic.StoreInt32(&atom, 1)
 112  	}
 113  }
 114  
 115  func stressInt32() func() {
 116  	var atom Int32
 117  	return func() {
 118  		atom.Load()
 119  		atom.Add(1)
 120  		atom.Sub(2)
 121  		atom.Inc()
 122  		atom.Dec()
 123  		atom.CAS(1, 0)
 124  		atom.Swap(5)
 125  		atom.Store(1)
 126  	}
 127  }
 128  
 129  func stressStdInt64() func() {
 130  	var atom int64
 131  	return func() {
 132  		atomic.LoadInt64(&atom)
 133  		atomic.AddInt64(&atom, 1)
 134  		atomic.AddInt64(&atom, -2)
 135  		atomic.AddInt64(&atom, 1)
 136  		atomic.AddInt64(&atom, -1)
 137  		atomic.CompareAndSwapInt64(&atom, 1, 0)
 138  		atomic.SwapInt64(&atom, 5)
 139  		atomic.StoreInt64(&atom, 1)
 140  	}
 141  }
 142  
 143  func stressInt64() func() {
 144  	var atom Int64
 145  	return func() {
 146  		atom.Load()
 147  		atom.Add(1)
 148  		atom.Sub(2)
 149  		atom.Inc()
 150  		atom.Dec()
 151  		atom.CAS(1, 0)
 152  		atom.Swap(5)
 153  		atom.Store(1)
 154  	}
 155  }
 156  
 157  func stressStdUint32() func() {
 158  	var atom uint32
 159  	return func() {
 160  		atomic.LoadUint32(&atom)
 161  		atomic.AddUint32(&atom, 1)
 162  		// Adding `MaxUint32` is the same as subtracting 1
 163  		atomic.AddUint32(&atom, math.MaxUint32-1)
 164  		atomic.AddUint32(&atom, 1)
 165  		atomic.AddUint32(&atom, math.MaxUint32)
 166  		atomic.CompareAndSwapUint32(&atom, 1, 0)
 167  		atomic.SwapUint32(&atom, 5)
 168  		atomic.StoreUint32(&atom, 1)
 169  	}
 170  }
 171  
 172  func stressUint32() func() {
 173  	var atom Uint32
 174  	return func() {
 175  		atom.Load()
 176  		atom.Add(1)
 177  		atom.Sub(2)
 178  		atom.Inc()
 179  		atom.Dec()
 180  		atom.CAS(1, 0)
 181  		atom.Swap(5)
 182  		atom.Store(1)
 183  	}
 184  }
 185  
 186  func stressStdUint64() func() {
 187  	var atom uint64
 188  	return func() {
 189  		atomic.LoadUint64(&atom)
 190  		atomic.AddUint64(&atom, 1)
 191  		// Adding `MaxUint64` is the same as subtracting 1
 192  		atomic.AddUint64(&atom, math.MaxUint64-1)
 193  		atomic.AddUint64(&atom, 1)
 194  		atomic.AddUint64(&atom, math.MaxUint64)
 195  		atomic.CompareAndSwapUint64(&atom, 1, 0)
 196  		atomic.SwapUint64(&atom, 5)
 197  		atomic.StoreUint64(&atom, 1)
 198  	}
 199  }
 200  
 201  func stressUint64() func() {
 202  	var atom Uint64
 203  	return func() {
 204  		atom.Load()
 205  		atom.Add(1)
 206  		atom.Sub(2)
 207  		atom.Inc()
 208  		atom.Dec()
 209  		atom.CAS(1, 0)
 210  		atom.Swap(5)
 211  		atom.Store(1)
 212  	}
 213  }
 214  
 215  func stressFloat64() func() {
 216  	var atom Float64
 217  	return func() {
 218  		atom.Load()
 219  		atom.CAS(1.0, 0.1)
 220  		atom.Add(1.1)
 221  		atom.Sub(0.2)
 222  		atom.Store(1.0)
 223  	}
 224  }
 225  
 226  func stressBool() func() {
 227  	var atom Bool
 228  	return func() {
 229  		atom.Load()
 230  		atom.Store(false)
 231  		atom.Swap(true)
 232  		atom.CAS(true, false)
 233  		atom.CAS(true, false)
 234  		atom.Load()
 235  		atom.Toggle()
 236  		atom.Toggle()
 237  	}
 238  }
 239  
 240  func stressString() func() {
 241  	var atom String
 242  	return func() {
 243  		atom.Load()
 244  		atom.Store("abc")
 245  		atom.Load()
 246  		atom.Store("def")
 247  		atom.Load()
 248  		atom.Store("")
 249  	}
 250  }
 251  
 252  func stressDuration() func() {
 253  	var atom = NewDuration(0)
 254  	return func() {
 255  		atom.Load()
 256  		atom.Add(1)
 257  		atom.Sub(2)
 258  		atom.CAS(1, 0)
 259  		atom.Swap(5)
 260  		atom.Store(1)
 261  	}
 262  }
 263  
 264  func stressError() func() {
 265  	var atom = NewError(nil)
 266  	var err1 = errors.New("err1")
 267  	var err2 = errors.New("err2")
 268  	return func() {
 269  		atom.Load()
 270  		atom.Store(err1)
 271  		atom.Load()
 272  		atom.Store(err2)
 273  		atom.Load()
 274  		atom.Store(nil)
 275  	}
 276  }
 277  
 278  func stressTime() func() {
 279  	var atom = NewTime(time.Date(2021, 6, 17, 9, 0, 0, 0, time.UTC))
 280  	var dayAgo = time.Date(2021, 6, 16, 9, 0, 0, 0, time.UTC)
 281  	var weekAgo = time.Date(2021, 6, 10, 9, 0, 0, 0, time.UTC)
 282  	return func() {
 283  		atom.Load()
 284  		atom.Store(dayAgo)
 285  		atom.Load()
 286  		atom.Store(weekAgo)
 287  		atom.Store(time.Time{})
 288  	}
 289  }
 290