当前位置: 首页 > news >正文

Go语言各种扩容机制(防止混淆)

Slice扩容

触发

使用append向Slice追加元素时,如果Slice空间不足,将会触发Slice扩容

原理

扩容实际上是重新分配一块更大的内存,将原Slice数据拷贝进新Slice,然后返回新Slice,扩容后再将数据追加进去。

机制

V1.8之前:

扩容容量的选择遵循以下规则:

  • 如果原Slice容量小于1024,则新Slice容量将扩大为原来的2倍;
  • 如果原Slice容量大于等于1024,则新Slice容量将扩大为原来的1.25倍;
// 1.17及以前的版本中
// old指切片的旧容量, cap指期望的新容量
func growslice(old, cap int) int {
    newcap := old
    doublecap := newcap + newcap
    // 如果期望容量大于旧容量的2倍,则直接使用期望容量作为最终容量
    if cap > doublecap {
        newcap = cap
    } else {
        // 如果旧容量小于1024,则直接翻倍
        if old < 1024 {
            newcap = doublecap
        } else {
            // 每次增长大约1.25倍
            for 0 < newcap && newcap < cap {
                newcap += newcap / 4
            }
            if newcap <= 0 {
                newcap = cap
            }
        }
    }
    // 这里忽略了对齐操作
    return newcap
}

V1.8之后:

新扩容容量的选择遵循以下规则:(拥有更平滑的扩容系数)

  • 如果原Slice容量小于256,则新Slice容量将扩大为原来的2倍;
  • 如果原Slice容量大于等于256,则新Slice容量将扩大为原来的  新容量 = (原容量+3*256)/4
// 只关心扩容规则的简化版growslice
func growslice(old, cap int) int {
    newcap := old
    doublecap := newcap + newcap
    if cap > doublecap {
        newcap = cap
    } else {
        const threshold = 256 // 不同点1
        if old < threshold {
            newcap = doublecap
        } else {
            for 0 < newcap && newcap < cap {
                newcap += (newcap + 3*threshold) / 4 // 不同点2
            }
            if newcap <= 0 {
                newcap = cap
            }
        }
    }
    return newcap
}

Map扩容

触发扩容的条件有二个:

  1. 负载因子 > 6.5时,也即平均每个bucket存储的键值对达到6.5个。增量扩容
  2. overflow数量 > 2^15时,也即overflow数量超过32768时。等量扩容/重排

注意:创建溢出桶不属于扩容机制

增量扩容

  • 当负载因子过大时,新开辟buckets空间,bucket数量为之前的 2 倍
  • 新空间被buckets引用,之前的旧空间被oldbuckets引用
  • 之后逐渐将 oldbuckets中的数据 搬迁到 新开辟的 buckets空间中去

考虑到如果map存储了数以亿计的key-value,一次性搬迁将会造成比较大的延时,Go采用逐步搬迁策略,即每次访问map时都会触发一次搬迁,每次搬迁2个键值对当oldbuckets中的键值对全部搬迁完毕后,删除oldbuckets。

下图展示了包含一个bucket满载的map(为了描述方便,图中bucket省略了value区域):

当前map存储了7个键值对,只有1个bucket。此时负载因子为7 > 6.5。再次插入数据时将会触发扩容操作,扩容之后再将新插入键写入新的bucket。注意,因为负载因子的触发,不是创建溢出桶

当第8个键值对插入时,将会触发扩容扩容后示意图如下:

后续对map的访问操作会触发迁移,将oldbuckets中的键值对逐步的搬迁过来。

搬迁完成后的示意图如下:

数据搬迁过程中原bucket中的键值对将存在于新bucket的前面,新插入的键值对将存在于新bucket的后面。

等量扩容/重排

所谓等量扩容,实际上并不是扩大容量,buckets数量不变,重新做一遍类似增量扩容的搬迁动作,把松散的键值对重新排列一次,以使bucket的使用率更高,进而保证更快的存取。
在极端场景下,比如不断地增删,而键值对正好集中在一小部分的bucket,这样会造成overflow的bucket数量增多,但负载因子又不高,从而无法执行增量搬迁的情况,如下图所示:

上图可见,overflow的bucket中大部分是空的,访问效率会很差。此时进行一次等量扩容,即buckets数量不变,经过重新组织后overflow的bucket数量会减少,即节省了空间又会提高访问效率。

相关文章:

  • Pytorch深度学习——线性回归实现 04(未完)
  • 虚拟内存、锁页内存、内存分页、分段、段页式内存管理(超详细)
  • 【BOOST C++】教程4:常量和宏
  • 不可以涩涩!AI续写软件初体验;迁移学习路线图;谷歌新闻非官方搜索API;CS295『因果推理』2021课程资料;前沿论文 | ShowMeAI资讯日报
  • 高项_第十四章信息文档管理与配置管理
  • 07 nginx 的 worker process 的调试
  • 时间序列预测:用电量预测 04 Std_Linear(多元线性回归算法 数据标准化)
  • boost之跨平台 错误处理
  • 【Shell编程】字符截取命令awk、sed命令
  • 阿里国际、ebay测评自养号,如何看待自己产品的销量?
  • FCOS网络详解
  • 前端异常监控系统
  • 混合开发架构|Android工程集成React Native、Flutter、ReactJs
  • Neo4j CQL
  • 【365天深度学习训练营】第三周 天气识别
  • 【技术性】Search知识
  • Angular Elements 及其运作原理
  • iOS仿今日头条、壁纸应用、筛选分类、三方微博、颜色填充等源码
  • JAVA SE 6 GC调优笔记
  • Java 多线程编程之:notify 和 wait 用法
  • JSONP原理
  • JS笔记四:作用域、变量(函数)提升
  • JS实现简单的MVC模式开发小游戏
  • k个最大的数及变种小结
  • PermissionScope Swift4 兼容问题
  • spring + angular 实现导出excel
  • tweak 支持第三方库
  • Webpack 4x 之路 ( 四 )
  • 半理解系列--Promise的进化史
  • 道格拉斯-普克 抽稀算法 附javascript实现
  • 翻译:Hystrix - How To Use
  • 关于 Linux 进程的 UID、EUID、GID 和 EGID
  • 用Node EJS写一个爬虫脚本每天定时给心爱的她发一封暖心邮件
  • 原生js练习题---第五课
  • Python 之网络式编程
  • 移动端高清、多屏适配方案
  • #我与Java虚拟机的故事#连载02:“小蓝”陪伴的日日夜夜
  • ( )的作用是将计算机中的信息传送给用户,计算机应用基础 吉大15春学期《计算机应用基础》在线作业二及答案...
  • (C语言)共用体union的用法举例
  • (ISPRS,2023)深度语义-视觉对齐用于zero-shot遥感图像场景分类
  • (二)Pytorch快速搭建神经网络模型实现气温预测回归(代码+详细注解)
  • (七)c52学习之旅-中断
  • (学习日记)2024.02.29:UCOSIII第二节
  • (一)Mocha源码阅读: 项目结构及命令行启动
  • (转)Java socket中关闭IO流后,发生什么事?(以关闭输出流为例) .
  • (转)大型网站的系统架构
  • (转)负载均衡,回话保持,cookie
  • (转)我也是一只IT小小鸟
  • . Flume面试题
  • .Net 8.0 新的变化
  • .NET Core/Framework 创建委托以大幅度提高反射调用的性能
  • .NET Framework 4.6.2改进了WPF和安全性
  • .net framework profiles /.net framework 配置
  • .Net 访问电子邮箱-LumiSoft.Net,好用
  • .net6解除文件上传限制。Multipart body length limit 16384 exceeded