/* * * Copyright 2017 gRPC authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ // Package primitives_test contains benchmarks for various synchronization primitives // available in Go. package primitives_test import ( "fmt" "sync" "sync/atomic" "testing" "time" "unsafe" ) func BenchmarkSelectClosed(b *testing.B) { c := make(chan struct{}) close(c) x := 0 b.ResetTimer() for i := 0; i < b.N; i++ { select { case <-c: x++ default: } } b.StopTimer() if x != b.N { b.Fatal("error") } } func BenchmarkSelectOpen(b *testing.B) { c := make(chan struct{}) x := 0 b.ResetTimer() for i := 0; i < b.N; i++ { select { case <-c: default: x++ } } b.StopTimer() if x != b.N { b.Fatal("error") } } func BenchmarkAtomicBool(b *testing.B) { c := int32(0) x := 0 b.ResetTimer() for i := 0; i < b.N; i++ { if atomic.LoadInt32(&c) == 0 { x++ } } b.StopTimer() if x != b.N { b.Fatal("error") } } func BenchmarkAtomicValueLoad(b *testing.B) { c := atomic.Value{} c.Store(0) x := 0 b.ResetTimer() for i := 0; i < b.N; i++ { if c.Load().(int) == 0 { x++ } } b.StopTimer() if x != b.N { b.Fatal("error") } } func BenchmarkAtomicValueStore(b *testing.B) { c := atomic.Value{} v := 123 b.ResetTimer() for i := 0; i < b.N; i++ { c.Store(v) } b.StopTimer() } func BenchmarkMutex(b *testing.B) { c := sync.Mutex{} x := 0 b.ResetTimer() for i := 0; i < b.N; i++ { c.Lock() x++ c.Unlock() } b.StopTimer() if x != b.N { b.Fatal("error") } } func BenchmarkRWMutex(b *testing.B) { c := sync.RWMutex{} x := 0 b.ResetTimer() for i := 0; i < b.N; i++ { c.RLock() x++ c.RUnlock() } b.StopTimer() if x != b.N { b.Fatal("error") } } func BenchmarkRWMutexW(b *testing.B) { c := sync.RWMutex{} x := 0 b.ResetTimer() for i := 0; i < b.N; i++ { c.Lock() x++ c.Unlock() } b.StopTimer() if x != b.N { b.Fatal("error") } } func BenchmarkMutexWithDefer(b *testing.B) { c := sync.Mutex{} x := 0 b.ResetTimer() for i := 0; i < b.N; i++ { func() { c.Lock() defer c.Unlock() x++ }() } b.StopTimer() if x != b.N { b.Fatal("error") } } func BenchmarkMutexWithClosureDefer(b *testing.B) { c := sync.Mutex{} x := 0 b.ResetTimer() for i := 0; i < b.N; i++ { func() { c.Lock() defer func() { c.Unlock() }() x++ }() } b.StopTimer() if x != b.N { b.Fatal("error") } } func BenchmarkMutexWithoutDefer(b *testing.B) { c := sync.Mutex{} x := 0 b.ResetTimer() for i := 0; i < b.N; i++ { func() { c.Lock() x++ c.Unlock() }() } b.StopTimer() if x != b.N { b.Fatal("error") } } func BenchmarkAtomicAddInt64(b *testing.B) { var c int64 b.ResetTimer() for i := 0; i < b.N; i++ { atomic.AddInt64(&c, 1) } b.StopTimer() if c != int64(b.N) { b.Fatal("error") } } func BenchmarkAtomicTimeValueStore(b *testing.B) { var c atomic.Value t := time.Now() b.ResetTimer() for i := 0; i < b.N; i++ { c.Store(t) } b.StopTimer() } func BenchmarkAtomic16BValueStore(b *testing.B) { var c atomic.Value t := struct { a int64 b int64 }{ 123, 123, } b.ResetTimer() for i := 0; i < b.N; i++ { c.Store(t) } b.StopTimer() } func BenchmarkAtomic32BValueStore(b *testing.B) { var c atomic.Value t := struct { a int64 b int64 c int64 d int64 }{ 123, 123, 123, 123, } b.ResetTimer() for i := 0; i < b.N; i++ { c.Store(t) } b.StopTimer() } func BenchmarkAtomicPointerStore(b *testing.B) { t := 123 var up unsafe.Pointer b.ResetTimer() for i := 0; i < b.N; i++ { atomic.StorePointer(&up, unsafe.Pointer(&t)) } b.StopTimer() } func BenchmarkAtomicTimePointerStore(b *testing.B) { t := time.Now() var up unsafe.Pointer b.ResetTimer() for i := 0; i < b.N; i++ { atomic.StorePointer(&up, unsafe.Pointer(&t)) } b.StopTimer() } func BenchmarkStoreContentionWithAtomic(b *testing.B) { t := 123 var c unsafe.Pointer b.RunParallel(func(pb *testing.PB) { for pb.Next() { atomic.StorePointer(&c, unsafe.Pointer(&t)) } }) } func BenchmarkStoreContentionWithMutex(b *testing.B) { t := 123 var mu sync.Mutex var c int b.RunParallel(func(pb *testing.PB) { for pb.Next() { mu.Lock() c = t mu.Unlock() } }) _ = c } type dummyStruct struct { a int64 b time.Time } func BenchmarkStructStoreContention(b *testing.B) { d := dummyStruct{} dp := unsafe.Pointer(&d) t := time.Now() for _, j := range []int{100000000, 10000, 0} { for _, i := range []int{100000, 10} { b.Run(fmt.Sprintf("CAS/%v/%v", j, i), func(b *testing.B) { b.SetParallelism(i) b.RunParallel(func(pb *testing.PB) { n := &dummyStruct{ b: t, } for pb.Next() { for y := 0; y < j; y++ { } for { v := (*dummyStruct)(atomic.LoadPointer(&dp)) n.a = v.a + 1 if atomic.CompareAndSwapPointer(&dp, unsafe.Pointer(v), unsafe.Pointer(n)) { n = v break } } } }) }) } } var mu sync.Mutex for _, j := range []int{100000000, 10000, 0} { for _, i := range []int{100000, 10} { b.Run(fmt.Sprintf("Mutex/%v/%v", j, i), func(b *testing.B) { b.SetParallelism(i) b.RunParallel(func(pb *testing.PB) { for pb.Next() { for y := 0; y < j; y++ { } mu.Lock() d.a++ d.b = t mu.Unlock() } }) }) } } } type myFooer struct{} func (myFooer) Foo() {} type fooer interface { Foo() } func BenchmarkInterfaceTypeAssertion(b *testing.B) { // Call a separate function to avoid compiler optimizations. runInterfaceTypeAssertion(b, myFooer{}) } func runInterfaceTypeAssertion(b *testing.B, fer interface{}) { x := 0 b.ResetTimer() for i := 0; i < b.N; i++ { if _, ok := fer.(fooer); ok { x++ } } b.StopTimer() if x != b.N { b.Fatal("error") } } func BenchmarkStructTypeAssertion(b *testing.B) { // Call a separate function to avoid compiler optimizations. runStructTypeAssertion(b, myFooer{}) } func runStructTypeAssertion(b *testing.B, fer interface{}) { x := 0 b.ResetTimer() for i := 0; i < b.N; i++ { if _, ok := fer.(myFooer); ok { x++ } } b.StopTimer() if x != b.N { b.Fatal("error") } }