// Copyright (c) 2016 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package atomic import ( "math" "runtime" "sync" "sync/atomic" "testing" ) const ( _parallelism = 4 _iterations = 1000 ) var _stressTests = map[string]func() func(){ "i32/std": stressStdInt32, "i32": stressInt32, "i64/std": stressStdInt32, "i64": stressInt64, "u32/std": stressStdUint32, "u32": stressUint32, "u64/std": stressStdUint64, "u64": stressUint64, "f64": stressFloat64, "bool": stressBool, "string": stressString, } func TestStress(t *testing.T) { for name, ff := range _stressTests { t.Run(name, func(t *testing.T) { defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(_parallelism)) start := make(chan struct{}) var wg sync.WaitGroup wg.Add(_parallelism) f := ff() for i := 0; i < _parallelism; i++ { go func() { defer wg.Done() <-start for j := 0; j < _iterations; j++ { f() } }() } close(start) wg.Wait() }) } } func BenchmarkStress(b *testing.B) { for name, ff := range _stressTests { b.Run(name, func(b *testing.B) { f := ff() b.Run("serial", func(b *testing.B) { for i := 0; i < b.N; i++ { f() } }) b.Run("parallel", func(b *testing.B) { b.RunParallel(func(pb *testing.PB) { for pb.Next() { f() } }) }) }) } } func stressStdInt32() func() { var atom int32 return func() { atomic.LoadInt32(&atom) atomic.AddInt32(&atom, 1) atomic.AddInt32(&atom, -2) atomic.AddInt32(&atom, 1) atomic.AddInt32(&atom, -1) atomic.CompareAndSwapInt32(&atom, 1, 0) atomic.SwapInt32(&atom, 5) atomic.StoreInt32(&atom, 1) } } func stressInt32() func() { var atom Int32 return func() { atom.Load() atom.Add(1) atom.Sub(2) atom.Inc() atom.Dec() atom.CAS(1, 0) atom.Swap(5) atom.Store(1) } } func stressStdInt64() func() { var atom int64 return func() { atomic.LoadInt64(&atom) atomic.AddInt64(&atom, 1) atomic.AddInt64(&atom, -2) atomic.AddInt64(&atom, 1) atomic.AddInt64(&atom, -1) atomic.CompareAndSwapInt64(&atom, 1, 0) atomic.SwapInt64(&atom, 5) atomic.StoreInt64(&atom, 1) } } func stressInt64() func() { var atom Int64 return func() { atom.Load() atom.Add(1) atom.Sub(2) atom.Inc() atom.Dec() atom.CAS(1, 0) atom.Swap(5) atom.Store(1) } } func stressStdUint32() func() { var atom uint32 return func() { atomic.LoadUint32(&atom) atomic.AddUint32(&atom, 1) // Adding `MaxUint32` is the same as subtracting 1 atomic.AddUint32(&atom, math.MaxUint32-1) atomic.AddUint32(&atom, 1) atomic.AddUint32(&atom, math.MaxUint32) atomic.CompareAndSwapUint32(&atom, 1, 0) atomic.SwapUint32(&atom, 5) atomic.StoreUint32(&atom, 1) } } func stressUint32() func() { var atom Uint32 return func() { atom.Load() atom.Add(1) atom.Sub(2) atom.Inc() atom.Dec() atom.CAS(1, 0) atom.Swap(5) atom.Store(1) } } func stressStdUint64() func() { var atom uint64 return func() { atomic.LoadUint64(&atom) atomic.AddUint64(&atom, 1) // Adding `MaxUint64` is the same as subtracting 1 atomic.AddUint64(&atom, math.MaxUint64-1) atomic.AddUint64(&atom, 1) atomic.AddUint64(&atom, math.MaxUint64) atomic.CompareAndSwapUint64(&atom, 1, 0) atomic.SwapUint64(&atom, 5) atomic.StoreUint64(&atom, 1) } } func stressUint64() func() { var atom Uint64 return func() { atom.Load() atom.Add(1) atom.Sub(2) atom.Inc() atom.Dec() atom.CAS(1, 0) atom.Swap(5) atom.Store(1) } } func stressFloat64() func() { var atom Float64 return func() { atom.Load() atom.CAS(1.0, 0.1) atom.Add(1.1) atom.Sub(0.2) atom.Store(1.0) } } func stressBool() func() { var atom Bool return func() { atom.Load() atom.Store(false) atom.Swap(true) atom.CAS(true, false) atom.CAS(true, false) atom.Load() atom.Toggle() atom.Toggle() } } func stressString() func() { var atom String return func() { atom.Load() atom.Store("abc") atom.Load() atom.Store("def") atom.Load() atom.Store("") } }