Home > Software design >  Efficiency comparation in `sync.Mutex`, `sync.Map`, and `atomic.Value`
Efficiency comparation in `sync.Mutex`, `sync.Map`, and `atomic.Value`

Time:03-28

When I compare efficiency of sync.mu, sync.Map and atomic.Value in go, expected that sync.mu is less efficient than latter two. But when I did a benchmark test, found that execution duration is less using sync.mu. So I want to know the reason or maybe there is a best practice.

Code

Source code is here https://go.dev/play/p/WODF8Tyyw4d Simplify it as follows:

  • Scene1: Change a random kv pair in map, sync.mu vs sync.Map
// sync.mu reader
mu.Lock()
tmp := tA[idx]
mu.Unlock()
// sync.mu writer
mu.Lock()
tA[idx] = data
mu.Unlock()

// sync.Map reader
v, _ := tB.Load(idx)
// sync.Map writer
tB.Store(idx, data)
  • Scene2: Change a whole map, sync.mu vs atomic.Value
// sync.mu reader
mu.Lock()
tmp := tA[0]
mu.Unlock()
// sync.mu Writer
mu.Lock()
tA = data
mu.Unlock()

// atomic.Value reader
v := tC.Load().(map[int]int)[0]
// atomic.Value writer
tC.Store(data)

Result

goos: darwin
goarch: amd64
pkg: sync-demo
cpu: Intel(R) Core(TM) i7-9750H CPU @ 2.60GHz
BenchmarkMu-12          1000000000               0.5401 ns/op
BenchmarkSyncMap-12     1000000000               0.5504 ns/op
BenchmarkMuTotal-12     1000000000               0.5418 ns/op
BenchmarkAtomic-12      1000000000               0.5457 ns/op
PASS
ok      sync-demo    64.195s

CodePudding user response:

You are not conparing the efficiency of such sync structures, since you are also doind I/0. Also there are goroutines and waitgroups in this code … not sure if I understand it

You should consider compare similar usages in the same context.

For instance, increment a counter. You have a counter in a sync.Map, atomic.Value and protected by mutex.

There are pros and cons of each approach, but the mutex handle only synchronization while the others structures also handles storage.

Mutex should be faster because it is… less complex.

However, if you handle something more complex than an uint64 perhaps the overhead of atomic.Value may be ok.

For instance, using mutex you need a specific order of lock/unlock and you may find some issues without proper testing race condition detector.

While atomic.Value handle this for you.

I never use sync.Map but I have very efficient code in production using atomic.Value - and I am ok with this.

The correct benchmark need a more technical approach.

  • Related