You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
401 lines
6.5 KiB
401 lines
6.5 KiB
/* |
|
* |
|
* 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") |
|
} |
|
}
|
|
|