ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

为什么 Go 的负载因子是 6.5?

2022-01-21 23:02:20  阅读:168  来源: 互联网

标签:map 负载 因子 6.5 哈希 Go


了解 map 底层
我以前在写《深入理解 Go map:初始化和访问元素》时有介绍过 map 的基础数据结构。

基本结构如下图:


map 基本数据结构
其中重要的一个基本单位是 hmap: 

type hmap struct {
 count     int
 flags     uint8
 B         uint8
 noverflow uint16
 hash0     uint32
 buckets    unsafe.Pointer
 oldbuckets unsafe.Pointer
 nevacuate  uintptr
 extra *mapextra
}
 
type mapextra struct {
 overflow    *[]*bmap
 oldoverflow *[]*bmap
 nextOverflow *bmap
}
count:map 的大小,也就是 len() 的值,代指 map 中的键值对个数。

flags:状态标识,主要是 goroutine 写入和扩容机制的相关状态控制。并发读写的判断条件之一就是该值。

B:桶,最大可容纳的元素数量,值为 负载因子(默认 6.5) * 2 ^ B,是 2 的指数。

noverflow:溢出桶的数量。

hash0:哈希因子。

buckets:保存当前桶数据的指针地址(指向一段连续的内存地址,主要存储键值对数据)。

oldbuckets,保存旧桶的指针地址。

nevacuate:迁移进度。

extra:原有 buckets 满载后,会发生扩容动作,在 Go 的机制中使用了增量扩容,如下为细项:

overflow 为 hmap.buckets (当前)溢出桶的指针地址。

oldoverflow 为 hmap.oldbuckets (旧)溢出桶的指针地址。

nextOverflow 为空闲溢出桶的指针地址。

我们关注到 hmap 的 B 字段,其值就是 6.5,他就是我们在苦苦寻找的 6.5,但他又是什么呢?

什么是负载因子
B 值,这里就涉及到一个概念:负载因子(load factor),用于衡量当前哈希表中空间占用率的核心指标,也就是每个 bucket 桶存储的平均元素个数。

另外负载因子与扩容、迁移等重新散列(rehash)行为有直接关系:

在程序运行时,会不断地进行插入、删除等,会导致 bucket 不均,内存利用率低,需要迁移。

在程序运行时,出现负载因子过大,需要做扩容,解决 bucket 过大的问题。

负载因子是哈希表中的一个重要指标,在各种版本的哈希表实现中都有类似的东西,主要目的是为了平衡 buckets 的存储空间大小和查找元素时的性能高低。

在接触各种哈希表时都可以关注一下,做不同的对比,看看各家的考量。

为什么是 6.5
了解是什么后,我们进一步深挖。

为什么 Go 语言中哈希表的负载因子是 6.5,为什么不是 8 ,也不是 1。这里面有可靠的数据支撑吗?

测试报告
实际上这是 Go 官方的经过认真的测试得出的数字,一起来看看官方的这份测试报告。

报告中共包含 4 个关键指标,如下:

loadFactor    %overflow    bytes/entry    hitprobe    missprobe
4.00    2.13    20.77    3.00    4.00
4.50    4.05    17.30    3.25    4.50
5.00    6.85    14.77    3.50    5.00
5.50    10.55    12.94    3.75    5.50
6.00    15.27    11.67    4.00    6.00
6.50    20.90    10.79    4.25    6.50
7.00    27.14    10.15    4.50    7.00
7.50    34.03    9.73    4.75    7.50
8.00    41.10    9.40    5.00    8.00
loadFactor:负载因子,也有叫装载因子。

%overflow:溢出率,有溢出 bukcet 的百分比。

bytes/entry:每对 key/elem 的开销字节数.

hitprobe:查找一个存在的 key 时,要查找的平均个数。

missprobe:查找一个不存在的 key 时,要查找的平均个数。

选择数值
结合测试报告一看,好家伙,不测不知道,一测吓一跳,有依据了。

Go 官方发现:负载因子太大了,会有很多溢出的桶。太小了,就会浪费很多空间(too large and we have lots of overflow buckets, too small and we waste a lot of space)。


来自 Go 官方源码说明
根据这份测试结果和讨论,Go 官方把 Go 中的 map 的负载因子硬编码为 6.5,这就是 6.5 的选择缘由。

这意味着在 Go 语言中,当 B(bucket)平均每个存储的元素大于或等于 6.5 时,就会触发扩容行为,这是作为我们用户对这个数值最近的接触。

总结
在今天这篇文章中,我们先快速了解了 Go 语言中 map 的基本数据结构和设计,这和我们要解释的问题紧密相关。

紧接着针对开头所提出的 6.5,进行了介绍和说明,这其实是 map 中的负载因子。其数值的确定来源于 Go 官方的测试。

为什么是 6.5,你懂了吗?

参考
src/runtime/map.go

深度解析golang map

golang中map底层B值的计算逻辑
————————————————
版权声明:本文为CSDN博主「煎鱼(EDDYCJY)」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/EDDYCJY/article/details/120359475

标签:map,负载,因子,6.5,哈希,Go
来源: https://blog.csdn.net/fish_study_csdn/article/details/122630868

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有