Golang 1.24 sync.Map性能飞跃:从源码剖析并发哈希前缀树实现原理

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%

说明:

  • MapLoadMostlyHits(主要命中情况) 变慢了一些(+6.93%),主要是因为 Swiss Tables 提升了旧版 sync.Map 的性能。

  • 某些测试看似大幅变慢,但主要原因是:

    • 新实现会立即收缩 map(当元素被删除时)。

    • 旧实现是分代收缩(需要 dirty map 触发提升)。

背景

并发哈希前缀树(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 数据结构

消息盒子

# 暂无消息 #

只显示最新10条未读和已读信息