Hacker News new | ask | show | jobs
by hknmtt 973 days ago
iirc RWMutex used to be much slower than standard Mutex so there was no good use case for it over standard Mutext. That might have gotten fixed, don't know.

Secondly, you can use atomics instead of mutexes. They will be faster by about 400% iirc.

Thirdly, you do not need to be concerned for readers, only for writers. Readers can concurrently read and they do not care if value changes. The only concern is writers because the can collide and that is where you need synchronizing. Protecting reads makes sense only in rare cases where you might have a map and it gets set to nil. So obviously reading from nil map will panic. Other than that there is not really much cases to protect readers and add locking overhead just for them.

2 comments

The benefit of an RWMutex lies in read-often, write-rare, where serialization of the portion under the read lock would significantly limit throughput (i.e., the read portion takes a while and overlaps significantly with other readers).

Whether atomics are applicable depend heavily on the structures and what you are doing under read lock. Atomics are good for reading primitives or pointers that get swapped whole, but not very useful for reason complex structures or traversing arrays/maps. Lock-free atomic structures exist, but are also often slower than their non-atomic counterparts.

Rather than making generic statements about what is slow and what is best, profile, profile, profile!

> iirc RWMutex used to be much slower than standard Mutex so there was no good use case for it over standard Mutext. That might have gotten fixed, don't know.

If you check the implementation RWMutex actually uses a Mutex underneath. The `RLock` is just an atomic increment if you don't have a writer.

The `Lock`(write lock) is normal `Mutex.Lock` + an atomic add.

> Secondly, you can use atomics instead of mutexes. They will be faster by about 400% iirc.

The golang community and the guys that actually implementing golang usually says `share via commuinication` and don't use any locks in the first place. You don't need even atomics in that case.

But in some cases, in production, you can measure and see that locking is better. I can admit that most of the projects can get away with not using locks and follow the guideline.

So I guess, if performance is important, try everything and measure.

> Thirdly, you do not need to be concerned for readers ...

Well, the use case was guarding multiple things under single lock. So the read lock needs to be there because you need a consistent view of the world. Otherwise, the reader see that a field is changed but another is not. And it is a bug for our case .

A simplified concrete example, Under write lock, user can call REDIS MSET command. https://redis.io/commands/mset/

Related part of the commands doc

``` MSET is atomic, so all given keys are set at once. It is not possible for clients to see that some of the keys were updated while others are unchanged. ``

So the readers should see that all updated or none updated. That is why for example one one can need a read lock.

Obviously, aside from the contract we have a lot going on in the background. Some of the data is in memory. some are loading. Some are just written in memory and dirty, therefore next read needs to wait for it to persisted to disk if it is evicted from memory etc....

All of these needs coordination between readers and writers.