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

70 阅读5分钟

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

引言:

1.性能优化的层面

  • 业务层优化:应该针对特定场景,具体问题具体分析。
  • 语言运行时优化:解决更通用的性能问题。
  • 数据驱动:可以通过自动化性能分析工具---pprof,同时进行性能优化应该依靠数据而非猜测,应首先优化最大瓶颈。

2.Go SDK分成两块:接口 实现 在保证接口稳定的前提下改进具体实现。
建议:用测试驱动开发 image.png

上述总结:

性能优化的基本问题
性能优化的两个层面:业务层方面、语言运行时优化
性能优化的可维护性

一、自动内存管理

常见的自动内存管理的方式:

  • Tracing garbage collection: 追踪垃圾回收
  • Reference counting: 引用计数
  • Generational GC: 分代 GC

1.自动内存管理的相关概念

  • Auto memory management: 自动内存管理
  • Grabage collction: 垃圾回收
  • Mutator: 业务线程
  • Collector: GC 线程
  • Concurrent GC: 并发 GC,mutators和collectors可以同时执行
    ,Collectors必须感知对象指向关系的改变。
  • Parallel GC: 并行 GC,会暂停,有多个collector进行回收
  • Serial GC: 会暂停,只有一个collector

image.png

2.评价GC算法 评价标准:吞吐量、最大暂停时间、堆的使用效率、访问的局部性

3.追踪垃圾回收
分为三个步骤:
①标记根对象
②标记:找到可达对象
③清理:所以不可达的对象
清理时有不同的清理策略:Copying GC(需要额外空间),Mark-sweep GC,mark-compact GC(原地整理) 如何选择不同的清理策略?根据对象的声明周期,选择不同的策略。

4.分代GC 将年龄(经历过GC的次数)不同的GC放置在不同的区域,这其实就是对年轻和老年的对象,制定不同的GC策略,降低整体的内存管理开销。 年轻代(Young generation):存活对象很少,可以采用copying collection。 老年代(Old generation):对象趋向于一直存活,反复复制开销较大,可以采用mask-sweep collection

5.引用计数
每个对象都有一个与之关联的引用数目,而对象存活的条件是:引用次数>0

二、Go内存管理及优化

前言:基本概念
为方便自动管理内存,可以先向系统预申请一块内存,然后再将内存分割成小块,通过一定的内存分配算法管理内存。
预分配的内存划分为①spans、②bitmap、③arena三部分。其中spans和bitmap都是为了管理arena区而存在的。
spans区域存放span的指针,每个指针对应一个或多个page,所以span区域的大小为(512G/8KB)*指针大小8byte=512M
②bitmap区域大小也是通过area计算出来,bug主要用于GC
③arena:堆区,应用中所需要的内存从此处分配。arena的大小为512G,将arena区域划分为一个个的page,每个page为8KB,一共有512GB/8KB个页。

1.Go内存分配
Go内存分配的思想:
①分块:目的是为对象在heap上分配内存
mspan分类:noscan mspan---分配不包含指针的对象和scan mspan---分配包含指针的对象
②缓存:借鉴了TCMalloc的思想

2.Go内存管理优化
Go内存分配存在的问题:对象分配是非常高频的操作、小对象占比较高

字节在Go SDK里面针对对象分配方面作出的优化方案:Balanced GC--本质是将多个小对象的分配合并成一次大对象的分配。
Balanced GC的细节:
GAB对于Go内存管理来说是个大对象,而GAB的对象分配方式会导致内存被延迟释放,对此可以移动GAB中存活的对象,也就是说当GAB总大小超过一定的阈值时,将GAB中存活的对象复制到另外分配的GAB中,而原先的GAB可以释放,避免内存泄露;这本质上是用copying GC算法来管理小对象。

三、编译器和静态分析

基本分析、数据流和控制流、过程内和过程间分析

1.基本分析

2.数据流和控制流的概念

  • 控制流分析:将程序控制流的信息提取出来,在做控制流分析的时候一般都会用控制流图进行分析
  • 数据流分析:分析数据在控制流上的传递

3.静态分析:不执行程序代码,推导程序的行为,分析程序的性质。控制流是指程序执行的流程,数据流是指数据在控制流上的传递。通过分析控制流和数据流,我们可以知道更多关于程序的性质。

4.过程内和过程间分析 过程内:仅在函数内部进行分析。 过程间:考虑函数调用时参数传递和返回值的数据流和控制流。

四、编译器优化

1.Go编译器优化的背景
最大的优点:用户无感知,重新编译就可以获得性能收益

2.函数内联
含义:将被调用函数的函数体的副本替换到调用位置上,同时重写代码以反映参数的绑定。
优点:

  • 消除函数的调用开销
  • 将过程间分析转化为过程内分析,帮助其他优化,例如逃逸分析。

在进行性能优化时,可以使用micro-benchmark快速验证和对比性能分析结果。

3.逃逸分析
含义:分析代码中指针的动态作用域,指针在何处可以被访问
大致步骤:
①从对象分配处出发,沿着控制流,观察对象的数据流
②若发现指针p在当前作用域s
③则指针p指向的对象逃逸出s,反之则没有逃逸出s