高性能Go语言发行版优化与落地实践 | 青训营笔记

78 阅读4分钟

高性能Go语言发行版优化与落地实践 | 青训营笔记


这是我参与「第五届青训营 」笔记创作活动的第4天

话题:性能优化


  • 目的:提升系统的处理能力,为用户带来体验的提升,实现对资源的高效利用。

  • 方向:业务层优化、语言运行时优化(本门课的方向)

目录


正文


自动内存管理

​ 动态内存:程序在运行时根据需求动态分配的内存。而对于动态分配的内存,难以做到手动的内存管理,因为难以做到内存使用的正确性和安全性。

​ 为此,引入了动态内存管理的概念。

image-20230119144022680

​ 而对于一个GC算法,我们关心其安全性,吞吐量,暂停时间,内存开销

常见GC算法
  • 追踪垃圾回收从根变量开始,求所有可达对象闭包,标记为存活,对死亡对象进行回收

    贯穿GC算法效率的核心:根据对象的生命周期,使用不同的标记和清理策略 image-20230119145413339

    • Copying GC

      image-20230119145205074

    • Mark-sweep GC image-20230119145319233

    • Compact GC image-20230119145356636

  • 分代GC(Generational GC)

    一个事实是:很多对象在分配之后不长时间就不再使用了,而我们可以定义年龄(对象经历GC的次数),依据年龄划分对象组(不同年龄的对象处于heap的不同位置)不同的GC策略,从而降低整体的内存管理开销。 image-20230119145926820

  • 引用计数

    每个对象都维护一个引用数目。当一个对象的的引用数目大于0时定义为存活。 优点:

    • 内存管理的开销被平摊到程序执行过程中
    • 内存管理也不需要了解runtime的实现细节

    缺点:

    • 维护引用计数开销大
    • 无法回收环形数据结构
    • 当引用层级复杂时,回收内存依旧可能发生STW

    image-20230119150819750

Go内存管理及优化

Go的内存管理
  • Go在堆上为对象分配内存的方法:提前进行内存分块,并在使用时根据对象的大小选择合适的内存块并返回。 image-20230119152221997
  • Go内存分配的缓存机制 p包含一个mcache,mchache管理一组mspan,当mchache中使用完毕后,可直接向mcentral申请没有分配块的mspan。当mspan上的对象被释放后,不会直接还给OS,而是会继续由mcentral缓存。 image-20230119155343789
Go的内存管理优化
Go内存管理的问题
  • 内存分配是相当高频的操作

  • 小对象的分配在总的分配占比非常高

  • Go的内存分配路径很长,导致内存分配耗时很高

优化方案:Balanced GC
image-20230119160108656
  • 在该方案下,GAB对于Go的内存管理而言就是一个大的对象,其目的就是减少多次小对象走长路径分配内存的次数。但是还是存在一个问题,就是当一个GAB只存在几个小对象时,对Go的内存管理而已,整个GAB大对象都是占用的,导致了内存的延迟释放。
  • 解决方案:统计GAB总大小,设定一个阈值,当大小超过阈值后,使用Copying GC策略(小对象使用此策略开销很小)重新分配小对象到新的GAB中,释放原先的GAB。 image-20230119160943214

编译器和静态分析

标准的编译器结构

image-20230119161058139

静态分析
  • 在编译阶段,由于无法执行代码和测试输入,可以在不执行代码的前提下进行静态分析优化编译。

  • 其中有两个考量的方面:控制流和数据流 image-20230119161325397

    • 根据控制流和数据流,我们可以优化代码,比如说下图左侧的代码因为输出恒为4,我们编译优化后直接使用return 4;代替原函数。 image-20230119161554594
  • 由静态分析,我们得出两大类分析:过程内分析、过程间分析 image-20230119161927892

    • 说人话就是,过程内不考虑输入输出,对函数的内部进行分析。而过程间考虑了调用时的输入输出,过程更加复杂。

Go编译器优化

函数内联(Inlining)
  • 说人话就是将调用的函数直接拷贝到调用处,使得两个函数间的调用转换为一个函数的调用。更重要的目的在于:将方便将过程间分析转化为过程内分析,减少静态分析的复杂性。

  • 内联的缺点

    • 编译生成的Go镜像将增大

    • 过于复杂和冗长的函数体会降低CPUcache的命中率

优化方案:Beast Mode
  • 方案的目的在于调整函数内联的策略,使得更多的函数被内联
  • 逃逸分析 image-20230119162949587

引用参考


‬⁢⁢⁣⁤⁤‍⁡⁣⁡⁡⁡⁣⁢⁤‌‌‌⁣⁣‍⁤‬⁣⁤‬⁤⁡⁤⁡⁡‍‌‬高性能 Go 语言发行版优化与落地实践 .pptx - 飞书云文档 (feishu.cn)