MapOf is compatible with sync.Map.
It draws heavily from xsync.MapOf
v3 and further implements fine-grained optimizations.
Demonstrates 10x higher performance than sync.Map in go 1.24
under large datasets
Unlike xsync.MapOf
:
-
Zero-initialization ready to use
-
Compatible with
sync.Map
andxsync.MapOf
, all tests passed. -
Further abstracted the logic, added functions such as
ProcessEntry
andIsZero
-
Faster than
xsync.MapOf
v3, at least as of March 10, 2025.
Must thank the authors of xsync for their contributions.
Below is an introduction to xsync.MapOf:
MapOf is like a Go map[K]V but is safe for concurrent
use by multiple goroutines without additional locking or
coordination. It follows the interface of sync.Map with
a number of valuable extensions like Compute or Size.
A MapOf must not be copied after first use.
MapOf uses a modified version of Cache-Line Hash Table (CLHT)
data structure: https://github.com/LPD-EPFL/CLHT
CLHT is built around idea to organize the hash table in
cache-line-sized buckets, so that on all modern CPUs update
operations complete with at most one cache-line transfer.
Also, Get operations involve no write to memory, as well as no
mutexes or any other sort of locks. Due to this design, in all
considered scenarios MapOf outperforms sync.Map.
MapOf also borrows ideas from Java's j.u.c.ConcurrentHashMap
(immutable K/V pair structs instead of atomic snapshots)
and C++'s absl::flat_hash_map (meta memory and SWAR-based lookups).
Below are my test results, focusing on pb_MapOf
goos: windows
goarch: amd64
cpu: AMD Ryzen Threadripper 3970X 32-Core Processor
BenchmarkLoad_original_syncMap
BenchmarkLoad_original_syncMap-64 1000000000 1.051 ns/op 0 B/op 0 allocs/op
BenchmarkLoadOrStore_original_syncMap
BenchmarkLoadOrStore_original_syncMap-64 135233187 8.029 ns/op 16 B/op 1 allocs/op
BenchmarkLoad_yyle88_syncmap
BenchmarkLoad_yyle88_syncmap-64 1000000000 1.096 ns/op 0 B/op 0 allocs/op
BenchmarkLoadOrStore_yyle88_syncmap
BenchmarkLoadOrStore_yyle88_syncmap-64 137798134 8.077 ns/op 16 B/op 1 allocs/op
BenchmarkLoad_pb_HashTrieMap
BenchmarkLoad_pb_HashTrieMap-64 1000000000 0.7279 ns/op 0 B/op 0 allocs/op
BenchmarkLoadOrStore_pb_HashTrieMap
BenchmarkLoadOrStore_pb_HashTrieMap-64 258239502 4.074 ns/op 0 B/op 0 allocs/op
BenchmarkLoadOrCompute_pb_HashTrieMap
BenchmarkLoadOrCompute_pb_HashTrieMap-64 306349586 3.787 ns/op 0 B/op 0 allocs/op
BenchmarkLoad_xsync_MapOf
BenchmarkLoad_xsync_MapOf-64 1000000000 0.3865 ns/op 0 B/op 0 allocs/op
BenchmarkLoadOrStore_xsync_MapOf
BenchmarkLoadOrStore_xsync_MapOf-64 1000000000 0.9971 ns/op 0 B/op 0 allocs/op
BenchmarkLoadOrCompute_xsync_MapOf
BenchmarkLoadOrCompute_xsync_MapOf-64 1000000000 1.257 ns/op 0 B/op 0 allocs/op
BenchmarkLoad_pb_MapOf
BenchmarkLoad_pb_MapOf-64 1000000000 0.3266 ns/op 0 B/op 0 allocs/op
BenchmarkLoadOrStore_pb_MapOf
BenchmarkLoadOrStore_pb_MapOf-64 1000000000 0.7637 ns/op 0 B/op 0 allocs/op
BenchmarkLoadOrCompute_pb_MapOf
BenchmarkLoadOrCompute_pb_MapOf-64 1000000000 0.7664 ns/op 0 B/op 0 allocs/op
BenchmarkLoad_alphadose_haxmap
BenchmarkLoad_alphadose_haxmap-64 1000000000 0.4163 ns/op 0 B/op 0 allocs/op
BenchmarkLoadOrStore_alphadose_haxmap
BenchmarkLoadOrStore_alphadose_haxmap-64 269435372 3.857 ns/op 8 B/op 1 allocs/op
BenchmarkLoad_zhangyunhao116_skipmap
BenchmarkLoad_zhangyunhao116_skipmap-64 706450005 1.745 ns/op 0 B/op 0 allocs/op
BenchmarkLoadOrStore_zhangyunhao116_skipmap
BenchmarkLoadOrStore_zhangyunhao116_skipmap-64 286135125 3.782 ns/op 0 B/op 0 allocs/op
BenchmarkLoad_fufuok_cmap
BenchmarkLoad_fufuok_cmap-64 172777294 7.003 ns/op 0 B/op 0 allocs/op
BenchmarkLoadOrStore_fufuok_cmap
BenchmarkLoadOrStore_fufuok_cmap-64 53934360 22.57 ns/op 0 B/op 0 allocs/op
BenchmarkLoad_mhmtszr_concurrent_swiss_map
BenchmarkLoad_mhmtszr_concurrent_swiss_map-64 147992079 8.216 ns/op 0 B/op 0 allocs/op
BenchmarkLoadOrStore_mhmtszr_concurrent_swiss_map
BenchmarkLoadOrStore_mhmtszr_concurrent_swiss_map-64 29085973 36.12 ns/op 0 B/op 0 allocs/op
BenchmarkLoad_easierway_concurrent_map
BenchmarkLoad_easierway_concurrent_map-64 109092314 10.93 ns/op 15 B/op 1 allocs/op
BenchmarkLoadOrStore_easierway_concurrent_map
BenchmarkLoadOrStore_easierway_concurrent_map-64 25202724 41.18 ns/op 24 B/op 2 allocs/op
BenchmarkLoad_orcaman_concurrent_map
BenchmarkLoad_orcaman_concurrent_map-64 100000000 10.27 ns/op 5 B/op 0 allocs/op
BenchmarkLoadOrStore_orcaman_concurrent_map
BenchmarkLoadOrStore_orcaman_concurrent_map-64 45814640 25.66 ns/op 5 B/op 0 allocs/op
BenchmarkLoad_RWLockMap
BenchmarkLoad_RWLockMap-64 34058785 34.70 ns/op 0 B/op 0 allocs/op
BenchmarkLoadOrStore_RWLockMap
BenchmarkLoadOrStore_RWLockMap-64 8990635 153.4 ns/op 0 B/op 0 allocs/op
BenchmarkLoad_RWLockShardedMap
BenchmarkLoad_RWLockShardedMap-64 100000000 10.20 ns/op 0 B/op 0 allocs/op
BenchmarkLoadOrStore_RWLockShardedMap
BenchmarkLoadOrStore_RWLockShardedMap-64 36439827 30.12 ns/op 0 B/op 0 allocs/op
PASS
Benchmarks code referenced from:
const MapElementCount = 100000
func BenchmarkLoad_original_syncMap(b *testing.B) {
b.ReportAllocs()
var m sync.Map
for i := 0; i < MapElementCount; i++ {
m.Store(i, i)
}
b.ResetTimer()
b.RunParallel(func(pb *testing.PB) {
i := 0
for pb.Next() {
_, _ = m.Load(i)
i++
if i >= MapElementCount {
i = 0
}
}
})
}
func BenchmarkLoadOrStore_original_syncMap(b *testing.B) {
b.ReportAllocs()
var m sync.Map
b.ResetTimer()
b.RunParallel(func(pb *testing.PB) {
i := 0
for pb.Next() {
_, _ = m.LoadOrStore(i, i)
i++
if i >= MapElementCount {
i = 0
}
}
})
}
import (
"github.com/llxisdsh/pb"
"math/rand/v2"
"testing"
)
func TestMapOf(t *testing.T) {
var m pb.MapOf[string, int]
m.LoadOrStore("a", 1)
m.Load("a")
m.LoadOrCompute("b", func() int {
return 2
})
m.ProcessEntry("a", func(e *pb.EntryOf[string, int]) (*pb.EntryOf[string, int], int, bool) {
if e != nil {
return nil, e.Value, true
}
return &pb.EntryOf[string, int]{Value: rand.Int()}, 0, false
})
t.Log(m.Size())
t.Log(m.IsZero())
t.Log(m.ToMap())
t.Log(&m)
m.Range(func(k string, v int) bool {
t.Log(k, v)
return true
})
// need go >= 1.23
for k, v := range m.Range {
t.Log(k, v)
}
}
Licensed under MIT.