A high-performance, thread-safe generic map for Go with a modern sync.Map-style API and an optimized sharded architecture.
| Feature | Description |
|---|---|
| API | Store / Load / Delete / LoadOrStore / LoadAndDelete / Range - aligned with sync.Map |
| Hash function | Folded-multiply hashing (internal) |
| Shard selection | & shardMask (power-of-two optimized) |
| Read path | Lock-free fast path via atomic.Pointer snapshot |
| False sharing protection | 64-byte cache-line padding per shard |
| Full scan | ParallelRange (parallel shard traversal) |
| Generics | Yes - ConcurrentMap[K, V] |
go get github.com/robby031/concurrent-mapimport cmap "github.com/robby031/concurrent-map"
// string key map
m := cmap.New[string]()
// Store a value
m.Store("user:1", "alice")
// Load a value
val, ok := m.Load("user:1")
// Load existing or store new
actual, loaded := m.LoadOrStore("user:1", "bob")
// Load and delete atomically
val, ok = m.LoadAndDelete("user:1")
// Delete
m.Delete("user:2")
// Iterate all entries (sequential, supports early-exit)
m.Range(func(key string, val string) bool {
fmt.Println(key, val)
return true // return false to stop early
})
// Iterate all entries (parallel, no early-exit - f must be goroutine-safe)
m.ParallelRange(func(key string, val string) {
fmt.Println(key, val)
})
// Count entries
n := m.Count()
// Clear all entries
m.Clear()
// JSON marshal / unmarshal
data, _ := json.Marshal(m)
json.Unmarshal(data, &m)// Any comparable type with a custom shard function
m := cmap.NewWithCustomShardingFunction[uint32, string](func(key uint32) uint32 {
return key
})
// Types implementing fmt.Stringer
type UserID int
func (u UserID) String() string { return strconv.Itoa(int(u)) }
m := cmap.NewStringer[UserID, string]()Default is 32. Must be a positive power of 2. Set before creating a map:
cmap.SHARD_COUNT = 64
m := cmap.New[string]()go test -bench=. -benchmem on Apple M4 (arm64, 10 cores):
BenchmarkSingleInsertAbsent-10 6,912,354 219 ns/op 107 B/op 3 allocs/op
BenchmarkSingleInsertAbsentSyncMap-10 6,519,684 282 ns/op 125 B/op 3 allocs/op
BenchmarkSingleInsertPresent-10 51,507,672 23.5 ns/op 24 B/op 1 allocs/op
BenchmarkSingleInsertPresentSyncMap-10 50,926,760 23.6 ns/op 48 B/op 1 allocs/op
BenchmarkMultiInsertSame-10 864,306 1407 ns/op 260 B/op 11 allocs/op
BenchmarkMultiInsertSameSyncMap-10 589,540 2126 ns/op 825 B/op 31 allocs/op
BenchmarkMultiGetSame-10 6,520,695 184 ns/op 16 B/op 1 allocs/op
BenchmarkMultiGetSameSyncMap-10 6,505,676 183 ns/op 16 B/op 1 allocs/op
BenchmarkMultiGetSetBlock_32_Shard-10 2,399,465 504 ns/op 304 B/op 12 allocs/op
BenchmarkMultiGetSetBlockSyncMap-10 1,888,932 642 ns/op 864 B/op 32 allocs/op
BenchmarkRange-10 20,392 58890 ns/op 62 B/op 1 allocs/op
BenchmarkParallelRange-10 38,808 30662 ns/op 1552 B/op 33 allocs/op
BenchmarkHash_Short-10 461,660,534 2.60 ns/op 0 B/op 0 allocs/op
BenchmarkHash_Medium-10 379,528,196 3.17 ns/op 0 B/op 0 allocs/op
Key takeaways:
- 22% faster than
sync.Mapfor absent-key writes - On par with
sync.Mapfor concurrent reads of the same key (lock-free path) - 21% faster than
sync.Mapfor mixed read/write workload (32 shards) - 34% faster for concurrent same-key writes (sharding eliminates single-mutex bottleneck)
- ParallelRange is 48% faster than sequential
Rangefor full scans
MIT