76 lines
1.6 KiB
Go
76 lines
1.6 KiB
Go
|
package shardedmutex
|
||
|
|
||
|
import (
|
||
|
"hash/maphash"
|
||
|
"sync"
|
||
|
)
|
||
|
|
||
|
const cacheline = 64
|
||
|
|
||
|
// padding a mutex to a cacheline improves performance as the cachelines are not contested
|
||
|
// name old time/op new time/op delta
|
||
|
// Locks-8 74.6ns ± 7% 12.3ns ± 2% -83.54% (p=0.000 n=20+18)
|
||
|
type paddedMutex struct {
|
||
|
mt sync.Mutex
|
||
|
pad [cacheline - 8]uint8
|
||
|
}
|
||
|
|
||
|
type ShardedMutex struct {
|
||
|
shards []paddedMutex
|
||
|
}
|
||
|
|
||
|
// New creates a new ShardedMutex with N shards
|
||
|
func New(n_shards int) ShardedMutex {
|
||
|
if n_shards < 1 {
|
||
|
panic("n_shards cannot be less than 1")
|
||
|
}
|
||
|
return ShardedMutex{
|
||
|
shards: make([]paddedMutex, n_shards),
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func (sm ShardedMutex) Shards() int {
|
||
|
return len(sm.shards)
|
||
|
}
|
||
|
|
||
|
func (sm ShardedMutex) Lock(shard int) {
|
||
|
sm.shards[shard].mt.Lock()
|
||
|
}
|
||
|
|
||
|
func (sm ShardedMutex) Unlock(shard int) {
|
||
|
sm.shards[shard].mt.Unlock()
|
||
|
}
|
||
|
|
||
|
func (sm ShardedMutex) GetLock(shard int) sync.Locker {
|
||
|
return &sm.shards[shard].mt
|
||
|
}
|
||
|
|
||
|
type ShardedMutexFor[K any] struct {
|
||
|
inner ShardedMutex
|
||
|
|
||
|
hasher func(maphash.Seed, K) uint64
|
||
|
seed maphash.Seed
|
||
|
}
|
||
|
|
||
|
func NewFor[K any](hasher func(maphash.Seed, K) uint64, n_shards int) ShardedMutexFor[K] {
|
||
|
return ShardedMutexFor[K]{
|
||
|
inner: New(n_shards),
|
||
|
hasher: hasher,
|
||
|
seed: maphash.MakeSeed(),
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func (sm ShardedMutexFor[K]) shardFor(key K) int {
|
||
|
return int(sm.hasher(sm.seed, key) % uint64(len(sm.inner.shards)))
|
||
|
}
|
||
|
|
||
|
func (sm ShardedMutexFor[K]) Lock(key K) {
|
||
|
sm.inner.Lock(sm.shardFor(key))
|
||
|
}
|
||
|
func (sm ShardedMutexFor[K]) Unlock(key K) {
|
||
|
sm.inner.Unlock(sm.shardFor(key))
|
||
|
}
|
||
|
func (sm ShardedMutexFor[K]) GetLock(key K) sync.Locker {
|
||
|
return sm.inner.GetLock(sm.shardFor(key))
|
||
|
}
|