`sync.Mutex`、`sync.Map`和`atomic.Value`的效率比较

huangapple go评论86阅读模式
英文:

Efficiency comparation in `sync.Mutex`, `sync.Map`, and `atomic.Value`

问题

当我比较Go语言中的sync.musync.Mapatomic.Value的效率时,预期sync.mu的效率低于后两者。但是当我进行了基准测试后,发现使用sync.mu的执行时间更短。所以我想知道原因,或者是否有最佳实践。

代码

源代码在这里:https://go.dev/play/p/WODF8Tyyw4d,简化如下:

  • 场景1:在map中更改一个随机的键值对,sync.mu vs sync.Map
// sync.mu 读取
mu.Lock()
tmp := tA[idx]
mu.Unlock()
// sync.mu 写入
mu.Lock()
tA[idx] = data
mu.Unlock()

// sync.Map 读取
v, _ := tB.Load(idx)
// sync.Map 写入
tB.Store(idx, data)
  • 场景2:更改整个map,sync.mu vs atomic.Value
// sync.mu 读取
mu.Lock()
tmp := tA[0]
mu.Unlock()
// sync.mu 写入
mu.Lock()
tA = data
mu.Unlock()

// atomic.Value 读取
v := tC.Load().(map[int]int)[0]
// atomic.Value 写入
tC.Store(data)

结果

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
英文:

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

答案1

得分: 1

您没有比较这些同步结构的效率,因为您还在进行I/O操作。此外,代码中还涉及到goroutine和waitgroup,我不确定是否理解。

您应该考虑在相同的上下文中比较类似的用法。

例如,递增一个计数器。您可以在sync.Map、atomic.Value和受互斥锁保护的计数器中进行比较。

每种方法都有其优缺点,但互斥锁仅处理同步,而其他结构还处理存储。

互斥锁应该更快,因为它更简单。

然而,如果您处理的内容比uint64更复杂,原子值的开销可能是可以接受的。

例如,使用互斥锁,您需要特定的锁定/解锁顺序,并且如果没有进行适当的测试和竞态条件检测,可能会遇到一些问题。

而atomic.Value会为您处理这些问题。

我从未使用过sync.Map,但我在生产环境中使用atomic.Value编写了非常高效的代码,并且对此感到满意。

正确的基准测试需要更多的技术方法。

英文:

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.

huangapple
  • 本文由 发表于 2022年3月27日 18:50:39
  • 转载请务必保留本文链接:https://go.coder-hub.com/71635678.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定