golang 的 map 实现(二)

本文探讨了Golang中map扩容的实现,包括overflow和hashGrow两种方式。overflow是bucket级别的扩容,而hashGrow涉及再散列。扩容过程中的数据迁移是异步的,并确保在扩容期间不会有并发增长。在数据迁移时,每次处理一个bucket,保证了map的正确性。此外,文章还提及了tophash在减少碰撞和控制比较消耗方面的作用。
摘要由CSDN通过智能技术生成

如何扩容

map 增长过程中,对空间的需求也在增加,那么如何完成透明扩容的同时,又不会太多影响性能,就是这里要讨论的。

仅考虑空间增长,map 的扩容方式有两种,overflow 以及 hashGrow

  • overflow 是溢出链,是 bucket 级别的扩容,理解成链表
  • hashGrow 则是再散列的实现。空间变大的同时,重新进行散列,不过 rehash 过程不是同步,而是被摊还到了 mapassign 以及 mapdelete变更 的操作上。同时,在这个过程中也涉及老空间的释放,如何释放的这个部分将放在下面 『如何迁移』里面讨论

overflow

map 存储数据的组织形式是通过 bucket 这个结构来做的。 然后在数据结构中的 h.bucket 是代表一组 bucket,其中的每个 bucket 与对应的 overflow 区域,组成了一个链表。后面用 表头 代表 h.bucket 中的 bucket, 用 bucket 代表 bucket 数据结构。 注意:这个表头不是一个 dummy 节点,也是要用来放数据的。

新的链表节点的增加是通过函数 newoverflow 实现的,主要工作就是先获取空间(可能是预先分配的,也有可能是从系统新申请),然后将其放在链表的末端。(针对 overflow[0] 的管理,目前不是很确定管理条件),对应代码和解释如下。

需要注意的就是:在针对预分配的空间和新申请空间的逻辑是有些不同的

func (h *hmap) newoverflow(t *maptype, b *bmap) *bmap {
    var ovf *bmap

    if h.extra != nil && h.extra.nextOverflow != nil {
        // 如果在预先创建了 overflow 区域,则先从本区域进行获取
        ovf = h.extra.nextOverflow
        if ovf.overflow(t) == nil {
            // overflow() 是读取的该 bucket 最后一个指针空间,是否有值
            // 有则代表预先申请的 overflow 区域已经用完,还记得 makeBucketArray 最后的设置吗?
            // 是保存的 h.buckets 的起始地址
            // 然后 nextOverFlow 维护预申请 overflow 域内的偏移量
            h.extra.nextOverflow = (*bmap)(add(unsafe.Pointer(ovf), uintptr(t.bucketsize)))
        } else {
            // 预先的已经用完了。此时的 ovf 代表了 overflow 内最后一个 bucket,将最后的指针位设置为 空
            // 并标记下预先申请的 overflow 区域已经用完
            ovf.setoverflow(t, nil) 
            h.extra.nextOverflow
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值