Skip to content

Commit

Permalink
feat: add spin-lock: func NewSpinLock() sync.Locker
Browse files Browse the repository at this point in the history
  • Loading branch information
fufuok committed Feb 5, 2025
1 parent 24dcfc0 commit b22b864
Show file tree
Hide file tree
Showing 2 changed files with 101 additions and 0 deletions.
30 changes: 30 additions & 0 deletions trylock.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
package utils

import (
"runtime"
"sync"
"sync/atomic"
"time"

"github.com/fufuok/utils/pools/timerpool"
Expand Down Expand Up @@ -44,3 +47,30 @@ func (m *TryMutex) TryLock(timeout ...time.Duration) bool {

}
}

// https://github.com/panjf2000/ants/blob/dev/pkg/sync/spinlock.go
type spinLock uint32

const maxBackoff = 16

func (sl *spinLock) Lock() {
backoff := 1
for !atomic.CompareAndSwapUint32((*uint32)(sl), 0, 1) {
// Leverage the exponential backoff algorithm, see https://en.wikipedia.org/wiki/Exponential_backoff.
for i := 0; i < backoff; i++ {
runtime.Gosched()
}
if backoff < maxBackoff {
backoff <<= 1
}
}
}

func (sl *spinLock) Unlock() {
atomic.StoreUint32((*uint32)(sl), 0)
}

// NewSpinLock instantiates a spin-lock.
func NewSpinLock() sync.Locker {
return new(spinLock)
}
71 changes: 71 additions & 0 deletions trylog_test.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package utils

import (
"runtime"
"sync"
"sync/atomic"
"testing"
"time"
)
Expand Down Expand Up @@ -76,3 +78,72 @@ func BenchmarkTrylock(b *testing.B) {
})
})
}

/*
Ref: https://github.com/panjf2000/ants/blob/dev/pkg/sync/spinlock_test.go
Benchmark result for three types of locks:
goos: darwin
goarch: arm64
pkg: github.com/panjf2000/ants/v2/pkg/sync
BenchmarkMutex-10 10452573 111.1 ns/op 0 B/op 0 allocs/op
BenchmarkSpinLock-10 58953211 18.01 ns/op 0 B/op 0 allocs/op
BenchmarkBackOffSpinLock-10 100000000 10.81 ns/op 0 B/op 0 allocs/op
*/

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 NewOriginSpinLock() sync.Locker {
return new(originSpinLock)
}

func BenchmarkSpinMutex(b *testing.B) {
m := sync.Mutex{}
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
m.Lock()
//nolint:staticcheck
m.Unlock()
}
})
}

func BenchmarkSpinLock(b *testing.B) {
spin := NewOriginSpinLock()
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
spin.Lock()
//nolint:staticcheck
spin.Unlock()
}
})
}

func BenchmarkBackOffSpinLock(b *testing.B) {
spin := NewSpinLock()
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
spin.Lock()
//nolint:staticcheck
spin.Unlock()
}
})
}

// # go test -run=^$ -benchmem -benchtime=1s -bench=Spin
// goos: linux
// goarch: amd64
// pkg: github.com/fufuok/utils
// cpu: AMD Ryzen 7 5700G with Radeon Graphics
// BenchmarkSpinMutex-16 23156875 54.36 ns/op 0 B/op 0 allocs/op
// BenchmarkSpinLock-16 215412934 5.564 ns/op 0 B/op 0 allocs/op
// BenchmarkBackOffSpinLock-16 246957524 4.877 ns/op 0 B/op 0 allocs/op

0 comments on commit b22b864

Please sign in to comment.