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

165 阅读5分钟

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

性能优化

  • 性能优化是什么

    • 提升软件系统处理能力,减少不必要的消耗,充分发掘计算机算力
  • 为什么要做性能优化

    • 用户体验:带来用户体验的提升
    • 资源高效利用:降低成本,提高效率——很小的优化乘以海量机器会是显著的性能提升和成本节约
    • 程序组件之间调用可能存在不必要的性能消耗
  • 优化层面 业务代码-sdk-基础库-语言运行时-os

    • 业务层优化 -针对特点场景、具体问题具体分析 -容易获得较大性能收益
    • 语言运行时优化
      • 考虑更通用的性能问题
      • 考虑更多场景
      • tradeoffs
    • 数据驱动
      • 使用自动化性能分析工具
      • 非猜测
      • 首先优化最大瓶颈
    • 优化与质量控制(可维护性)
      • 测试用例 尽可能覆盖多的场景 方便回归
      • 文档 做了什么没做什么 方便客户决定是否开启优化
      • 功能优化可观测:必要的日志输出

截屏2022-05-14 下午6.57.29.png

自动内存管理

  • 动态内存:根据运行时动态分配 malloc()
  • 垃圾回收: 由程序语言的运行时系统管理动态内存
    • 避免手动内存管理,专注于实现业务逻辑
    • 保证内存使用的正确性和安全性
  • 三个任务
    • 为新对象分配空间
    • 找到存活对象(程序还会使用的)
    • 回收死亡对象的内存空间
  • concurrently
    • Mutator:业务线程,分配新对象,修改对象指向关系
    • Collector GC线程,找到存活对象,回收死亡对象的内存空间
    • 回收算法
      • Serial GC:只有一个collector,把mutator都pause,然后用一个collector回收垃圾,回收结束后再继续mutators的执行
      • Parallel GC:支持多个collectors同时回收的GC算法
      • Concurrent GC:mutator(s)& collector(s)可以同时执行,一边做gc一边做mutator
        • o标记了,b没标记(gc过程中业务线程还在执行,创建了新的b对象),但concurrent gc要能感知对象指向关系的改变,把b也标记上(三色标记,混合写膨胀??)

截屏2022-05-14 下午7.12.38.png

截屏2022-05-14 下午7.08.07.png

-暂停时间越小越好-用户感知不到最好 截屏2022-05-14 下午7.15.14.png

tracing garbage collection追踪垃圾回收

  • 对象被回收的条件:指针指向关系不可达的对象
  • 三步骤
    • 标记根对象
      • 静态变量、全局变量、常量、线程栈等
    • 标记: 找到可达对象
      • 求指针指向关系的传递闭包,从根对象出发
    • 清理:所有不可达对象,清理策略
      • 复制copying GC(复制存活对象到额外开的新空间)
      • 标记清理,将死亡对象的内存标记为“可分配” (Marking sweep GC)freelist管理空闲内存
      • Mark-compact GC, 移动并整理存活对象(原地整理)类copy但不开新空间
    • 根据对象的生命周期,使用不同的标记和清理策略
    • 分代GC -分代假说:大部分对象很快就死了,用完了就回收了 -年龄:经历GC的次数
      • 目的:新生代,老年代 制定不同gc策略,降低整体内存管理的开销,
      • 新老分别放在两个不同的内存空间中, 不同年龄处于heap的不同区域
        • 新生代
          • 常规的对象分配
          • 存活对象很少,可以copying collection
          • gc吞吐率很高
        • 老年代
          • 趋于一直活着,反复复制开销较大

          • marking-sweep collection

引用计数

  • 每个对象都有一个与之关联的引用数目
  • 对象存活的条件:当且仅当引用数大于0
  • 优点
    • 内存管理的操作被平摊到程序执行过程中,程序边运行 边进行引用计数管理
    • 内存管理不需要了解runtime的实现细节:C++智能指针(引用计数的内存管理 自动帮你做)
  • 缺点
    • 维护引用计数的开销较大,通过原子操作保证引用计数操作的原子性和可见性
    • 无法回收环形数据结构
    • 内存开销: 每个对象都引入的额外内存空间 存储引用数目
    • 回收内存时依然可能引发暂停, null-引用链上是大数据结构 -白色圈 引用不可达-被清理 截屏2022-05-14 下午7.50.43.png

截屏2022-05-14 下午7.56.48.png 截屏2022-05-14 下午7.55.17.png

go内存管理及优化

  • 分块,目标:
    • 为对象在heap上分配内存
    • 提前分块
    • 对象分配:根据对象的大小,选择合适的块返回
    • gc扫描-指针指向的所有对象扫描 截屏2022-05-14 下午7.59.52.png
  • 缓存
    • 多级缓存
    • 第一级找不到内存可分配-》去下一级缓存找

截屏2022-05-14 下午8.02.54.png

  • 优化
    • 原因, 对象分配是高频操作,小对象占比高-小对象特级优化,内存分配很耗时耗cpu
    • 字节方案- balanced GC

截屏2022-05-14 下午8.05.00.png

截屏2022-05-14 下午8.22.05.png

截屏2022-05-14 下午8.22.48.png

小对象移动清理GAB

截屏2022-05-14 下午8.23.52.png

编译器和静态分析

截屏2022-05-14 下午8.35.37.png 静态分析:不执行程序代码,推导程序的行为,分析程序的性质

  • 控制流 程序执行的流程
  • 数据流 数据在控制流上的传递
  • 分析得到更多关于程序的性质——进而优化代码
  • 过程内分析:仅在函数内部
  • 过程间分析,考虑调用时参数传递和返回值的数据流和控制流

Go编译器优化

  • 为什么
    • 用户无感知
    • 通用性优化

截屏2022-05-14 下午8.42.52.png

截屏2022-05-14 下午8.43.45.png

截屏2022-05-14 下午8.45.55.png

截屏2022-05-14 下午8.47.10.png