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

93 阅读3分钟

这是我参与「第三届青训营 -后端场」笔记创作活动的的第3篇笔记

Go语言优化

什么是性能优化?为什么要做性能优化?

一个观点:性能优化是充分挖掘计算机的算力,减少不必要的消耗。做性能优化既能提高用户体验,又能降低开销。

性能优化层面

业务代码 -> SDK -> 基础库 -> 语言运行时 -> OS。

业务层代码很好优化,因为有具体场景,还是普通开发写的。

语言运行时优化要考虑的方面相对多,要综合所有使用者做权衡。

注意,优化一定要基于数据。要看大性能瓶颈,抓住主要矛盾。同时,优化也要保证软件质量,尽量不影响其他接口,一定要保证稳定性。

自动内存管理

自动内存管理一方面可以降低心智负担,另一方面可以保证内存使用的正确性。

比较常见的问题是 double free 和 use after free。

相关概念

Mutator 用户线程,申请空间,修改引用关系。

Collector GC:垃圾回收线程

截屏2022-05-12 上午12.50.33.png

注意,前两者会暂停。最后的不会。最后的是Concurrently GC。

Concurrently GC它的挑战很大。因为你要感知Mutator对引用关系的改变

可达性分析算法

第一步,标记root对象。这里涉及到GC ROOTS。比如静态变量、全局变量、常量、栈上对象。这些对象之所以可以当作root是因为之后可能还要用。

第二步,标记可达对象。就是递归标记。

第三步,清理不可达对象。标记清除、复制、收缩。

引用计数算法

这是我见过的最好的引用计数算法的ppt。

截屏2022-05-12 上午1.00.37.png

比如weak reference,可以解决引用计数的弊端,比如swift。

Go内存管理与优化

Go的内存分配做法:提前申请一大块内存然后分成小块。

分块

截屏2022-05-12 上午1.05.27.png

类似OS里的SLAB。注意这里的包含指针的对象块(scan mspan),这里提到ta主要是指GC的时候需要顺着他tracing。

缓存

截屏2022-05-12 上午1.09.27.png

意思就是先找大块,大块里找个小块。找不到去找下个大块。如果一个缓存里的所有大块都用没了,向central要一个大块。和银杏树的SLAB差不多。

优化——Balanced GC

截屏2022-05-12 上午1.11.48.png

截屏2022-05-12 上午1.13.44.png

这看起来不是很酷··因为我理解这就是TLAB,我以为是更牛逼的东西。Go的对象没有对象头,需要额外数据结构去记录一个GAB里有哪些对象,对象的起始地址之类的(用位图)。

但事实上,把东西讲得简单本身就是高手才能做到的。不应该眼高手低,也不应该因为有人做过就否定字节的Balanced GC。 其实他很酷。

截屏2022-05-12 上午1.17.39.png

这里确实很酷,应用标记复制思想去清理内存。这个阶段发生在最后的垃圾清理阶段。

解释一下这里的必要性。假设我们有两个GAB,占用内存2KB。里面的存活对象可能只有1B。在这种时候,我们也无法让GC回收这两KB的空间。我们需要把这1B存活对象拷贝到新的GAB中,才能回收这两KB。这两KB里有1999B都是垃圾。

编译器和静态分析

截屏2022-05-12 上午1.35.40.png

静态分析

截屏2022-05-12 上午1.42.14.png 在控制流上传播数据流的信息,可以做程序优化。

过程内分析和过程间分析:一个是只在函数内,一个是考虑函数的入参、出参。

截屏2022-05-12 上午1.44.11.png

Go编译器优化

截屏2022-05-12 上午1.45.35.png

虽然很有意思但是··这好像在开倒车。go就是不想让编译慢,你故意弄慢多做点优化。。

函数内联

截屏2022-05-12 上午1.47.43.png

同样地,这也是我第一次见到函数内联的缺点。

截屏2022-05-12 上午1.49.33.png

逃逸分析

截屏2022-05-12 上午1.53.13.png