Swiss Maps and maphash

Updated

September 8, 2026

Swiss Maps and maphash

Overview

Go’s map implementation has evolved. Recent versions moved toward Swiss Table-style layouts (open addressing with group metadata) for better CPU cache behavior versus older bucket-chain designs. Exact rollout is versioned — treat this as a performance model, not a guarantee of on-disk layout.

Complement: Slice & map internals.

Diagram: Map lookup path

  key ──hash──► probe groups / buckets ──► slot
  high load ──► grow / evacuate

  maphash.Seed ──► custom tables / sharding

What Changed Conceptually

Legacy (simplified):  buckets -> overflow lists
Swiss-style:          groups of slots + control bytes (SIMD-friendly probes)

Implications:

  • Better average lookup locality
  • Different resize/evacuation behavior
  • Iteration order still randomized — never depend on it
  • Concurrent map misuse still fatal

Practical Advice (Unchanged)

m := make(map[string]T, approxSize) // still helps
  • Pre-size when cardinality known
  • Prefer string / integer keys over allocations as keys
  • Do not store huge structs as values if you update fields often (consider map[K]*T)

maphash

Package hash/maphash provides high-quality, process-randomized hashing suitable for your own hash tables and sharding:

import "hash/maphash"

var seed maphash.Seed // zero value ok; or maphash.MakeSeed()

func hashString(s string) uint64 {
    var h maphash.Hash
    h.SetSeed(seed)
    h.WriteString(s)
    return h.Sum64()
}

Properties:

  • Seed differs per process → harder hash-flooding for custom maps
  • Not a crypto hash
  • Fast for strings/bytes

Use for:

  • Sharding keys across workers
  • Custom open-address tables
  • Consistent bucketing within one process (not cross-process stable unless you fix a seed carefully and accept risks)

Map Iteration and Deletes

for k := range m {
    if shouldDrop(k) {
        delete(m, k) // allowed in range
    }
}

Experiment

go mod init example
go test -bench=. -benchmem
package mapspeed_test

import (
    "hash/maphash"
    "testing"
)

func BenchmarkMapString(b *testing.B) {
    m := make(map[string]int, b.N)
    for i := 0; i < b.N; i++ {
        m[string(rune('a'+i%26))+string(rune(i))] = i
    }
}

func BenchmarkMaphash(b *testing.B) {
    var seed maphash.Seed
    var h maphash.Hash
    s := "authorization"
    b.ResetTimer()
    for i := 0; i < b.N; i++ {
        h.SetSeed(seed)
        h.WriteString(s)
        _ = h.Sum64()
        h.Reset()
    }
}

What to notice: Built-in maps are heavily optimized; custom maps need maphash + careful probing to approach them.

Try next: Read Go release notes for your version mentioning map changes; re-run production pprof after upgrades.