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

74 阅读4分钟

这是我参与「第三届青训营 -后端场」笔记创作活动的的第3篇笔记 主要记录了GO语言优化,编译器和静态分析,Go编译器优化

GO语言优化

自动内存管理

动态内存:程序在运行时根据需求时动态分配的内存

自动内存管理:由程序语言的运行时系统管理动态内存

  • 避免手动内存管理,专注于实现业务逻辑
  • 保证内存使用的正确性和安全性

三个任务

  • 分配新空间
  • 寻找存活对象
  • 回收死对象

相关概念

  • mutator:业务现场,分配新对象,修改对象指向关系
  • Collector:GC线程,寻找存活对象,回收死亡对象
  • Serial GC:只有一个collector
  • Parallel GC:有多个collector,同时执行GC算法
  • Concurrent GC:mutator和collector可以同时执行

如何判断对象死亡?

追踪垃圾回收(可达性分析)

标记根对象

标记可达对象

清理所有不可达对象(3个gc算法)

根据具体需求选择不同gc算法

gc依据:分代假说

引用计数法

每个对象都有与之关联的引用数目,当引用数目<=0时回收该对象。

内存优化

内存分配-分块

目标:将对象在heap上分配内存

提前将内存分块

  • 调用系统调用mmap()想os申请一块大内存
  • 将大内存分大块,称为mspan
  • 再将大块分成小块,用于对象分配
  • noscan mspan:分配不包含指针的对象
  • scan mspan:分配包含指针的对象

对象分配,根据对象的大小,选择最合适的内存分块

内存分配-缓存

TCMalloc:thread caching

G:Goroutine

M:Processor,逻辑处理器

P:Machine,系统物理线程

每个p包含一个mcache用于快速分配,用于为绑定于p上的goroutine分配对象

mcache管理一组mspan

当mcache中的mspan分配完毕,向mcentral申请带有未分配的mspan

当mspan中没有分配的对象,mspan会被缓存在mcentral中,而不是立刻释放还给os

balanced gc

  • 每个g都绑定一块大内存(GAB)
  • GAB用于noscan类型的小对象分配
  • 使用三个指针维护GAB:base,end,top
  • Bump pointer风格对象分配

无需和其他分配请求互斥

分配动作简单高效

 if top + size <=end{
   addr:=top
   top+=size
   return addr
 }

本质:将对个小对象分配合成一次大对象的分配

问题:GBA对象分配方式会导致内存被延迟释放

方案:移动GAB中存活的对象

本质:copying gc来管理小对象

基本思想:根据对象的生命周期来管理对象。

编译器和静态分析

编译器结构

重要的系统软件

  • 识别符合语法和非法的程序
  • 生成正确且高效的代码

分析部分(前端 front end)

  • 词法分析,生成词素
  • 语法分析,生成语法树
  • 语义分析,收集类型信息,进行语义检查
  • 中间代码生成,生成intermediate representatin

综合部分(后端 back end)

  • 代码优化,机器无关优化,生成优化后的IR
  • 代码生成,生成目标代码

静态分析

静态分析:不执行代码,推导程序执行

性质

  • 控制流:程序执行流程
  • 数据流:数据在控制流上传递

过程内和过程间分析

过程内:仅在函数内部分析

过程间:考虑过程吊桶式参数的传递和返回值的数据流和控制流

Go编译器优化

函数内联

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

优点:

  • 消除函数调用开销,例如参数传递,返回值等等
  • 将过程间转化为过程内分析,帮组其他优化。

缺点:

  • 函数体变大,icache不友好
  • 编译生成的go镜像变大

在大多数情况下,函数内联是正向的

Beast Mode

调整函数内敛策略,使更多函数被内联

  • 降低函数调用开销
  • 增加其他优化的机会

逃逸分析

分析指针的动态作用域:指针在何处可以被访问

当前p指针在s作用域:

  • 作为参数传递给其他函数
  • 传递给全局变量
  • 传递给其他的goroutine
  • 传递给已经逃逸的对象

则p逃逸出s,反之没有逃逸

beast mode拓展了函数边界,使更多对象不逃逸

优化: 未逃逸的对象可以在栈上分配

对象在栈上分配和回收很快:移动sp

减少heap上的分配,降低gc负担