| /* |
| * |
| * 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") |
| } |
| } |