Fundamentals 13 min read

Can Go 1.24’s Swiss Map Really Boost Performance? A Deep Dive into Its Design

This article examines Go 1.24’s new Swiss map implementation, covering its compatibility with the legacy map, the extendible hashing mechanism that enables incremental expansion, performance improvements, and remaining challenges such as concurrency limits and memory fragmentation.

Radish, Keep Going!
Radish, Keep Going!
Radish, Keep Going!
Can Go 1.24’s Swiss Map Really Boost Performance? A Deep Dive into Its Design

Compatibility: Seamless Migration Support

Go’s Swiss map is designed to be compatible with the existing map type. Conditional compilation tags and type conversion allow a painless switch between the old and new implementations. For example, in export_swiss_test.go the newTestMapType function directly casts the metadata of a traditional map to the Swiss map type.

//https://github.com/golang/go/blob/3f4164f508b8148eb526fc096884dba2609f5835/src/internal/runtime/maps/export_swiss_test.go#L14
func newTestMapType[K comparable, V any]() *abi.SwissMapType {
    var m map[K]V
    mTyp := abi.TypeOf(m)
    mt := (*abi.SwissMapType)(unsafe.Pointer(mTyp)) // 直接类型转换
    return mt
}

This design enables existing code to enable the Swiss map via the experimental flag GOEXPERIMENT=swissmap without code changes. To keep using the old map, set GOEXPERIMENT=noswissmap.

Swiss Map Data Structure

Extendible Hashing: How Dynamic Expansion Works

The core innovation of the Swiss map is the use of Extendible Hashing to support efficient incremental expansion. Traditional hash tables require full rehashing on resize, whereas Extendible Hashing splits tables incrementally using a multi‑level directory.

Dir and Table Hierarchy

In map.go the Map struct contains globalDepth and directory. The directory size is 1 << globalDepth, each entry points to a table. When a table’s load exceeds maxTableCapacity (default 1024), a split is triggered.

//https://github.com/golang/go/blob/3f4164f508b8148eb526fc096884dba2609f5835/src/internal/runtime/maps/map.go#L194
type Map struct {
    globalDepth  uint8 // dir的全局深度
    dirPtr       unsafe.Pointer // dir指针(指向多个 table)
    // ...
}

Split Operation

During a split, the original table (e.g., table A) creates two child tables ( Left and Right) whose localDepth is one greater than the original. The split redistributes entries based on the high‑order bits of the hash determined by localDepthMask.

//https://github.com/golang/go/blob/3f4164f508b8148eb526fc096884dba2609f5835/src/internal/runtime/maps/table.go#L1043
func (t *table) split(typ *abi.SwissMapType, m *Map) {
    localDepth := t.localDepth
    localDepth++ // 子表的 localDepth 比原表大 1
    left := newTable(typ, maxTableCapacity, -1, localDepth)
    right := newTable(typ, maxTableCapacity, -1, localDepth)
    // ...
}

Directory Update and Expansion

After splitting, the global directory may need to double in size if the original table’s localDepth equals the current globalDepth. The directory entries are then updated to point to the new tables.

// map.go
func (m *Map) installTableSplit(old, left, right *table) {
    if old.localDepth == m.globalDepth {
        // 目录扩展:大小翻倍
        newDir := make([]*table, m.dirLen*2)
        for i := range m.dirLen {
            newDir[2*i] = left
            newDir[2*i+1] = right
        }
        m.dirPtr = unsafe.Pointer(&newDir[0])
        m.globalDepth++
    } else {
        // 不扩展目录,仅替换部分项
        entries := 1 << (m.globalDepth - left.localDepth)
        for i := 0; i < entries; i++ {
            m.directorySet(uintptr(old.index+i), left)
            m.directorySet(uintptr(old.index+i+entries), right)
        }
    }
}

Key Design Advantages

Locality : Only heavily loaded tables are split, leaving others untouched.

Incremental Expansion : The directory grows on demand, avoiding a massive one‑time migration.

Address Continuity : New tables allocate a contiguous memory block for groups.data, improving cache performance.

Other Optimizations

The Swiss map uses a single group to store up to eight elements, reducing overhead for small maps.

Legacy Issues and Challenges

Concurrency Limitations

The current implementation uses a simple writing flag to detect concurrent writes, lacking fine‑grained locking. This can cause race conditions under high contention.

//https://github.com/golang/go/blob/3f4164f508b8148eb526fc096884dba2609f5835/src/internal/runtime/maps/map.go#L478
func (m *Map) PutSlot(typ *abi.SwissMapType, key unsafe.Pointer) unsafe.Pointer {
    m.writing ^= 1 // 简单标志位,非原子操作
    // ...
}

Memory Fragmentation

The group structure (8 control bytes + 8 key/value slots) can waste space when keys and values are small, e.g., int32 keys with int8 values.

Iterator Complexity

The iterator must handle directory expansion and table splits, which adds overhead in frequently resizing scenarios.

// https://github.com/golang/go/blob/3f4164f508b8148eb526fc096884dba2609f5835/src/internal/runtime/maps/table.go#L742
func (it *Iter) Next() {
    if it.globalDepth != it.m.globalDepth {
        // 处理目录扩展后的索引调整
        it.dirIdx <<= (it.m.globalDepth - it.globalDepth)
    }
    // ...
}

Performance Testing

Benchmarks from the author’s gomapbench repository show an average 28 % speedup, with peaks up to 50 % in some workloads. However, the tests are limited to a single laptop and some reports indicate performance regressions in certain cases.

Conclusion

Go 1.24’s Swiss map brings significant performance gains in high‑load scenarios through compatibility design, Extendible Hashing, and targeted optimizations. Nevertheless, its concurrency model and memory efficiency still have room for improvement, so developers should evaluate it carefully for performance‑critical code.

References

https://tonybai.com/2024/11/14/go-map-use-swiss-table/

https://github.com/golang/go/issues/54766

https://pub.huizhou92.com/swisstable-a-high-performance-hash-table-implementation-3e13bfe8c79b

https://www.geeksforgeeks.org/extendible-hashing-dynamic-approach-to-dbms/

performanceGohash tableExtendible HashingSwiss Map
Radish, Keep Going!
Written by

Radish, Keep Going!

Personal sharing

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.