day3后端与性能优化 | 青训营笔记

73 阅读3分钟

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

主要是学习了Go语言发行版优化与落地实践,理解了Go语言内存管理和编译器后端的代码优化。

Go语言优化

  • 内存管理
  • 编译器

一、介绍

  1. 为什么:提升系统处理能力、减少不必要消耗,改善用户体验、高效利用资源

  2. 两个层面:

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

a. 业务层

  • 针对特定场景
  • 易获得较大性能收益

b. 语言运行时

  • 解决通用性能问题
  • Tradeoffs

c. 数据驱动

  • 自动化性能分析工具 pprof
  • 依靠数据而非猜测
  • 首先优化最大瓶颈

Go SDK

接口 - Commands - APIs - New APIs

实现 - Complier - Scheduler - GC - Runtime - Libs - Profiling

  1. 性能优化的可维护性:

保证接口稳定的前提下改进具体实现

测试用例:尽可能多的场景 - 测试驱动开发

文档:做了什么,没做什么,能达到怎样的效果

隔离:选项控制是否开启优化

可观测:必要日志输出

二、自动内存管理(垃圾回收)

动态内存:根据需求分配的内存

由程序语言运行时系统管理动态内存,既可以专注于实现业务逻辑,又可以保证内存的正确性和安全性。

三个任务:为新对象分配空间,找到存活对象,回收死亡对象的内存空间

Mutator threads 业务线程:分配新对象,修改对象指向关系;

Collector GC线程:找到存活对象,回收死亡对象的内存空间;

Serial GC:只有一个collector;

Parallel GC:支持多个collectors同时回收GC;

Concurrent GC:mutators和collector可以同时执行;Collectors必须感知对象指向关系变化。

评价:

  • 安全性:不能回收存活的对象;
  • 吞吐率:1-GC时间/程序执行时间;
  • 暂退时间:业务是否感知;
  • 内存开销:GC元数据开销;

技术:

  • 追踪垃圾回收 - 指针对象不可达对象 - 标记&清理(Copying GC、Mark-sweep GC、Mark-compact GC)

根据对象生命周期,使用不同标记和清理策略

分代GC - 一种内存管理方式

分代假说:most objects die young 对象:经历GC的册数 - young(copying 吞吐率高) & old generation(mark-sleep)

  • 引用计数 - 对象存活条件:大于0

优点:内存管理平摊到程序执行过程中,不需要了解runtime实现细节;

缺点:维护开销较大(原子操作),无法回收环形数据结构,内存开销,回收内存时可能引发暂停。

三、内存分配

分块:为对象在heap上分配内存 - 根据对象大小、选择最合适的块返回;

缓存:TCMalloc - thread caching

非常高频、每秒GB、小对象占比较高、Go内存分配路径过程耗时。

Balanced GC

GAB(goroutine allocation buffer)

用于noscan类型的小对象(<128B)

三个维护指针:base,end,top

将多个小对象的分配合并成一次大对象的分配(copying GC)

编译器

源代码 - 词法分析器 - 语法分析器 - 语义分析器 - 中间表示 - 代码优化 - 代码生成 - 目标代码

静态分析:不执行程序代码,推导程序行为 - 控制流 & 数据流