`sync.Mutex`、`sync.Map` 和 `atomic.Value` 中的效率比较
Efficiency comparation in `sync.Mutex`, `sync.Map`, and `atomic.Value`
当我在go中比较sync.mu
、sync.Map
和atomic.Value
的效率时,预计sync.mu
比后两者效率低。但是当我做一个基准测试时,发现使用 sync.mu
执行时间更少。所以我想知道原因或者可能有最佳实践。
代码
源代码在这里https://go.dev/play/p/WODF8Tyyw4d简化如下:
- 场景1:改变地图中的随机kv对,
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)
- 场景2:换一张全图,
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)
结果
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
你不是在比较这种同步结构的效率,因为你也在做 I/0。这段代码中还有 goroutines 和 waitgroups …不知道我是否理解
您应该考虑比较相同上下文中的相似用法。
例如,增加一个计数器。您在 sync.Map、atomic.Value 中有一个计数器并受互斥锁保护。
每种方法各有利弊,但互斥体只处理同步,而其他结构也处理存储。
Mutex 应该更快,因为它……不那么复杂。
但是,如果您处理的东西比 uint64 更复杂,那么 atomic.Value 的开销可能没问题。
例如,使用互斥锁需要特定的 lock/unlock 顺序,如果没有适当的测试 + 竞争条件检测器,您可能会发现一些问题。
虽然 atomic.Value 为您处理。
我从不使用 sync.Map 但我在生产中使用 atomic.Value 的代码非常高效 - 我对此没有意见。
正确的基准需要更多的技术方法。
当我在go中比较sync.mu
、sync.Map
和atomic.Value
的效率时,预计sync.mu
比后两者效率低。但是当我做一个基准测试时,发现使用 sync.mu
执行时间更少。所以我想知道原因或者可能有最佳实践。
代码
源代码在这里https://go.dev/play/p/WODF8Tyyw4d简化如下:
- 场景1:改变地图中的随机kv对,
sync.mu
vssync.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)
- 场景2:换一张全图,
sync.mu
vsatomic.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)
结果
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
你不是在比较这种同步结构的效率,因为你也在做 I/0。这段代码中还有 goroutines 和 waitgroups …不知道我是否理解
您应该考虑比较相同上下文中的相似用法。
例如,增加一个计数器。您在 sync.Map、atomic.Value 中有一个计数器并受互斥锁保护。
每种方法各有利弊,但互斥体只处理同步,而其他结构也处理存储。
Mutex 应该更快,因为它……不那么复杂。
但是,如果您处理的东西比 uint64 更复杂,那么 atomic.Value 的开销可能没问题。
例如,使用互斥锁需要特定的 lock/unlock 顺序,如果没有适当的测试 + 竞争条件检测器,您可能会发现一些问题。
虽然 atomic.Value 为您处理。
我从不使用 sync.Map 但我在生产中使用 atomic.Value 的代码非常高效 - 我对此没有意见。
正确的基准需要更多的技术方法。