Skip to content

Commit

Permalink
format
Browse files Browse the repository at this point in the history
  • Loading branch information
kelindar committed Jun 23, 2021
1 parent cdc5742 commit 7e3a116
Show file tree
Hide file tree
Showing 3 changed files with 217 additions and 202 deletions.
17 changes: 16 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,22 @@

This package contains a sharded mutex which *should* do better than a traditional `sync.RWMutex` in certain cases where you want to protect resources that are well distributed. For example, you can use this to protect a hash table as keys have no relation to each other. That being said, for the hash table use-case you should probably use `sync.Map`.

The `SMutex128` works by actually creating 128 `sync.RWMutex` and providing `Lock()`, `Unlock()` methods that accept a shard argument. A shard argument can overflow the actual number of shards, and mutex uses a modulus operation to wrap around.
The `SMutex128` works by actually creating 128 `sync.RWMutex` and providing `Lock()`, `Unlock()` methods that accept a `shard` argument. A shard argument can overflow the actual number of shards, and mutex uses a modulus operation to wrap around.


```go
// Acquire a write lock for shard #1
mu.Lock(1)
resourceInShard1 = "hello"

// Release the lock in shard #1
mu.Unlock(1)
```

## Caveats

* Sharded mutex would use significantly more memory and needs to be used with care. In fact, the 128 shard implementation would use 8192 bytes of memory, and would ideally be living in L1. The reason being is that the current implementation pads mutexes so only one of them is present in a cache line, to prevent false sharing.
* Do benchmark on your own use-case if you want to use this library, I found that in certain cases the current implementation does not perform very well, but need to investigate a bit more. It's usually on-par with the `RWMutex` at the very least.

## Benchmarks

Expand Down
80 changes: 40 additions & 40 deletions smutex.go
Original file line number Diff line number Diff line change
@@ -1,40 +1,40 @@
// Copyright (c) Roman Atachiants and contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for details.

package smutex

import "sync"

const shards = 128

// SMutex128 represents a sharded RWMutex that supports finer-granularity concurrency
// contron hence reducing potential contention.
type SMutex128 struct {
mu [shards]struct {
sync.RWMutex
_ [40]byte // Padding to prevent false sharing
}
}

// Lock locks rw for writing. If the lock is already locked for reading or writing,
// then Lock blocks until the lock is available.
func (rw *SMutex128) Lock(shard uint) {
rw.mu[shard%shards].Lock()
}

// Unlock unlocks rw for writing. It is a run-time error if rw is not locked for
// writing on entry to Unlock.
func (rw *SMutex128) Unlock(shard uint) {
rw.mu[shard%shards].Unlock()
}

// RLock locks rw for reading. It should not be used for recursive read locking; a
// blocked Lock call excludes new readers from acquiring the lock.
func (rw *SMutex128) RLock(shard uint) {
rw.mu[shard%shards].RLock()
}

// RUnlock undoes a single RLock call and does not affect other simultaneous readers.
func (rw *SMutex128) RUnlock(shard uint) {
rw.mu[shard%shards].RUnlock()
}
// Copyright (c) Roman Atachiants and contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for details.

package smutex

import "sync"

const shards = 128

// SMutex128 represents a sharded RWMutex that supports finer-granularity concurrency
// contron hence reducing potential contention.
type SMutex128 struct {
mu [shards]struct {
sync.RWMutex
_ [40]byte // Padding to prevent false sharing
}
}

// Lock locks rw for writing. If the lock is already locked for reading or writing,
// then Lock blocks until the lock is available.
func (rw *SMutex128) Lock(shard uint) {
rw.mu[shard%shards].Lock()
}

// Unlock unlocks rw for writing. It is a run-time error if rw is not locked for
// writing on entry to Unlock.
func (rw *SMutex128) Unlock(shard uint) {
rw.mu[shard%shards].Unlock()
}

// RLock locks rw for reading. It should not be used for recursive read locking; a
// blocked Lock call excludes new readers from acquiring the lock.
func (rw *SMutex128) RLock(shard uint) {
rw.mu[shard%shards].RLock()
}

// RUnlock undoes a single RLock call and does not affect other simultaneous readers.
func (rw *SMutex128) RUnlock(shard uint) {
rw.mu[shard%shards].RUnlock()
}

0 comments on commit 7e3a116

Please sign in to comment.