Go 1.24 内置 map 的瑞士表(Swiss Table)到底怎么工作
来源: victoriametrics.com — 2026-09-03
概述
VictoriaMetrics 用一篇文章图文并茂地拆解 Go 1.24 替换旧实现后的 map 运行时:它基于 Swiss Table 设计。map 的运行时表示是指向 internal/runtime/maps.Map 的指针,Map 顶部的 used 让 len(m) 以 O(1) 取到,seed 让不同 map 用不同随机种子散列同一键而分布不同。最小编存单元是 group,容纳 8 个键值槽 + 8 个控制字节(合成一个 uint64 控制字);64 位散列被拆成 H1 与 7 位的 H2,H2 存入槽上方控制字节、最高位标记空/墓碑状态。查找时用 H2 经 SIMD(AMD64)一次比 8 个槽筛出候选,再对候选做完整键等值比较,从而不必逐个读全键。容量增长时先扩成 2/4/8…组的 table(上限 128 组 1024 槽),按三角探测序列找空槽;超过 1024 槽则把某张 table 一分为二,目录(dir) 用 H1 最高位选择 table、低位选择组,配合 global/local depth 决定是否扩展目录。相比旧版溢出桶链与逐桶渐进搬移,新版只重建触发增长的那张 table;Go 团队微基准显示 map 操作最快快 60%,应用整体几何均值省约 1.5% CPU。文中还讲删除的墓碑语义、7/8 负载因子的理由,以及 Go 1.27 实验性的 split-group 布局(键值分离以提升查找局部性并省 padding)。
核心要点
- map 变量是指向 runtime Map 的指针,used 字段让 len(m) 为 O(1),随机 seed 让同一键在不同 map 中分布不同
- 最小单元 group 装 8 个键值槽与 8 个控制字节;散列分成 H1(选起始组)与 7 位 H2(存进控制字节)
- 查找用 H2 在 AMD64 上以 SIMD 一次比较 8 个控制字节筛出候选,再对候选做完整键比较,不必逐槽读全键
- 增长先把 1 组扩成多组的 table(每 table 最多 128 组 1024 槽),用三角探测序列找空槽,满了再按 H1 位拆 table
- 目录用 H1 高位选 table、低位选组,借助 global/local depth 让单表增长不必重建整张 map
- 相比旧版溢出桶链,新版只重建需要扩容的那张表;微基准 map 操作最快提速 60%,应用整体约省 1.5% CPU
- Go 1.27 实验性 split-group 布局把键与值分开存放,提升键查找局部性并为 struct{} 值省掉对齐填充
金句
In the Go team's microbenchmarks, map operations ran up to 60% faster than in Go 1.23.(在 Go 团队的微基准中,map 操作比 Go 1.23 快了最多 60%。)
👍 0
👎 0
返回 Lobsters 首页