diff --git a/internal/spinlock.go b/internal/spinlock.go index 16607ef..af8b55f 100644 --- a/internal/spinlock.go +++ b/internal/spinlock.go @@ -1,4 +1,4 @@ -// Copyright 2019 Andy Pan. All rights reserved. +// Copyright 2019 Andy Pan & Dietoad. All rights reserved. // Use of this source code is governed by an MIT-style // license that can be found in the LICENSE file. @@ -13,8 +13,12 @@ import ( type spinLock uint32 func (sl *spinLock) Lock() { + backoff := 1 for !atomic.CompareAndSwapUint32((*uint32)(sl), 0, 1) { - runtime.Gosched() + for i := 0; i < backoff; i++ { + runtime.Gosched() + } + backoff <<= 1 } } diff --git a/internal/spinlock_test.go b/internal/spinlock_test.go new file mode 100644 index 0000000..577037f --- /dev/null +++ b/internal/spinlock_test.go @@ -0,0 +1,78 @@ +// Copyright 2021 Andy Pan & Dietoad. All rights reserved. +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file. + +package internal + +import ( + "runtime" + "sync" + "sync/atomic" + "testing" +) + +type originSpinLock uint32 + +func (sl *originSpinLock) Lock() { + for !atomic.CompareAndSwapUint32((*uint32)(sl), 0, 1) { + runtime.Gosched() + } +} + +func (sl *originSpinLock) Unlock() { + atomic.StoreUint32((*uint32)(sl), 0) +} + +func GetOriginSpinLock() sync.Locker { + return new(originSpinLock) +} + +type backOffSpinLock uint32 + +func (sl *backOffSpinLock) Lock() { + wait := 1 + for !atomic.CompareAndSwapUint32((*uint32)(sl), 0, 1) { + for i := 0; i < wait; i++ { + runtime.Gosched() + } + wait <<= 1 + } +} + +func (sl *backOffSpinLock) Unlock() { + atomic.StoreUint32((*uint32)(sl), 0) +} + +func GetBackOffSpinLock() sync.Locker { + return new(backOffSpinLock) +} + +func BenchmarkMutex(b *testing.B) { + m := sync.Mutex{} + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + m.Lock() + m.Unlock() + } + }) +} + +func BenchmarkSpinLock(b *testing.B) { + spin := GetOriginSpinLock() + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + spin.Lock() + spin.Unlock() + } + }) +} + +func BenchmarkBackOffSpinLock(b *testing.B) { + spin := GetBackOffSpinLock() + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + spin.Lock() + spin.Unlock() + } + }) +}