1. 前言
Golang在1.24中使用并发哈希前缀树修改了sync.Map
的实现,大幅提升了性能,本文从源码出发,分析sync.Map在1.24中的实现细节。
2. 官方描述
sync.Map
的实现已更改为 并发哈希前缀树(Concurrent Hash-Trie),这提升了性能,特别是在 修改 map 时 的表现更优。对于 不相交的 key 集合 进行修改时,在较大的 map 上竞争的可能性更小,同时新实现不再需要 预热时间 来实现低竞争负载。
新实现在几乎所有基准测试中都优于旧实现:
基准测试 | 旧实现 (sec/op) | 新实现 (sec/op) | 变化幅度 |
MapLoadMostlyHits | 7.870ns ± 1% | 8.415ns ± 3% | 0.0693 |
MapLoadMostlyMisses | 7.210ns ± 1% | 5.314ns ± 2% | -26.28% |
MapLoadOrStoreBalanced | 360.10ns ± 18% | 71.78ns ± 2% | -80.07% |
MapLoadOrStoreUnique | 707.2ns ± 18% | 135.2ns ± 4% | -80.88% |
MapLoadOrStoreCollision | 5.089ns ± 201% | 3.963ns ± 1% | -22.11% |
MapLoadAndDeleteBalanced | 17.045ns ± 64% | 5.280ns ± 1% | -69.02% |
MapLoadAndDeleteUnique | 14.250ns ± 57% | 6.452ns ± 1% | ~ (p=0.368) |
MapLoadAndDeleteCollision | 19.34ns ± 39% | 23.31ns ± 27% | ~ (p=0.180) |
MapRange | 3.055µs ± 3% | 1.918µs ± 2% | -37.23% |
MapAdversarialAlloc | 245.30ns ± 6% | 14.90ns ± 23% | -93.92% |
MapAdversarialDelete | 143.550ns ± 2% | 8.184ns ± 1% | -94.30% |
MapDeleteCollision | 9.199ns ± 65% | 3.165ns ± 1% | -65.59% |
MapSwapCollision | 164.7ns ± 7% | 108.7ns ± 36% | -34.01% |
MapSwapMostlyHits | 33.12ns ± 15% | 35.79ns ± 9% | ~ (p=0.180) |
MapSwapMostlyMisses | 604.5ns ± 5% | 280.2ns ± 7% | -53.64% |
MapCompareAndSwapCollision | 96.02ns ± 40% | 69.93ns ± 24% | -27.17% |
MapCompareAndSwapNoExistingKey | 6.345ns ± 1% | 6.202ns ± 1% | -2.24% |
MapCompareAndSwapValueNotEqual | 6.121ns ± 3% | 5.564ns ± 4% | -9.09% |
MapCompareAndSwapMostlyHits | 44.21ns ± 13% | 43.46ns ± 11% | ~ (p=0.485) |
MapCompareAndSwapMostlyMisses | 33.51ns ± 6% | 13.51ns ± 5% | -59.70% |
MapCompareAndDeleteCollision | 27.85ns ± 104% | 31.02ns ± 26% | ~ (p=0.180) |
MapCompareAndDeleteMostlyHits | 50.43ns ± 33% | 109.45ns ± 8% | 1.1703 |
MapCompareAndDeleteMostlyMisses | 27.17ns ± 7% | 11.37ns ± 3% | -58.14% |
MapClear | 300.2ns ± 5% | 124.2ns ± 8% | -58.64% |
几何平均值 (geomean) | 50.38ns | 25.79ns | -48.81% |
说明:
背景
并发哈希前缀树(HashTrieMap) 最初是在 Go 1.23 中引入的,作为 unique
包的一部分。由于它在许多情况下比 sync.Map
更快,Go 团队决定 重写 sync.Map
作为 HashTrieMap
的包装器。
如何禁用?
如果需要恢复旧版 sync.Map
,可以在 构建时 设置环境变量:
GOEXPERIMENT=nosynchashtriemap
3. 实现原理
本文依据Golang1.24.0版本的源码展开分析,不同版本或许存在差异。核心代码处于internal/sync/hashtriemap.go
。
3.1 数据结构