英文:
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:在map中更改一个随机的键值对,
sync.mu
vssync.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
vsatomic.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
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)
- Scene2: Change a whole map,
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)
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.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论