3 ms·
I ran benchmarks comparing xsync.Map's memory allocation against orcaman/concurrent-map. Pure overwrite workload (pre-allocated values): xsync.Map:
by tl2do 8mo ago
I ran benchmarks comparing xsync.Map's memory allocation against orcaman/concurrent-map.
Pure overwrite workload (pre-allocated values):
xsync.Map: 24 B/op 1 alloc/op 31.89 ns/op
orcaman/concurrent-map: 0 B/op 0 alloc/op 70.72 ns/op
Real-world mixed (80% overwrites, 20% new):
xsync.Map: 57 B/op 2 allocs/op 218.1 ns/op
orcaman/concurrent-map: 63 B/op 3 allocs/op 283.1 ns/op
Go maps reuse memory on overwrites, which is why orcaman achieves 0 B/op for pure updates. xsync's custom bucket structure allocates 24 B/op per write even when overwriting existing keys.
At 1M writes/second with 90% overwrites: xsync allocates ~27 MB/s, orcaman ~6 MB/s. The trade is 24 bytes/op for 2x speed under contention. Whether this matters depends on whether your bottleneck is CPU or memory allocation.
Benchmark code: standard Go testing framework, 8 workers, 100k keys.
- hinkley 8mo agoHow does reuse avoid false sharing between cores? Since this is concurrent hashmap we are talking about.
- tl2do 8mo agoI focused on B/op because it was the only apparent weakness I saw. My “reuse” note was about allocation behavior, not false sharing. We’re talking about different concerns.
- hinkley 8mo agoAllocation behavior when one core deletes and another adds and they reuse the same memory allocation is what I thought you meant. Is that what you meant? Because if it is then you now have potential for the problem I described.
- puzpuzpuz-hn 8mo agoAllocation rates comparison is included. If your application writes into the map most of the time, you should go with plain map + RWMutex (or orcaman/concurrent-map). But if, for instance, you're using the map as a cache, read operations will dominate and having better read scalability becomes important. As an example, Otter cache library uses a modified variant of xsync.Map, not a plain map + RWMutex.