编译器和静态分析 | 青训营笔记

87 阅读2分钟

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

编译器和静态分析

编译器的结构

image.png

编译器优化

目的

  • 用户无感知,重新编译即可获得性能收益
  • 通用的优化手段

现状

  • 采用的优化较少
  • 追求编译时间短,因此没有进行复杂的代码分析和优化

思路

  • 面向后端长期执行的任务
  • 用适当增加编译时间换取更高性能的代码

优化

函数内联

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

    • 消除调用开销
    • 将过程间分析的问题转换为过程内分析,帮助其他分析
  • 缺点

    • 函数体变大
    • 编译生成的 Go 镜像文件变大
  • 函数内联在大多数情况下是正向优化,即多内联,会提升性能
  • 采取一定的策略决定是否内联

    • 调用和被调用函数的规模
  • Go 内联的限制

    • 语言特性:interface, defer 等等,限制了内联优化
    • 内联策略非常保守
  • 字节跳动的优化方案

    • 修改了内联策略,让更多函数被内联
    • 增加了其他优化的机会:逃逸分析
  • 开销

    • Go 镜像大小略有增加
    • 编译时间增加
    • 运行时栈扩展开销增加

逃逸分析

  • 定义:分析代码中指针的动态作用域,即指针在何处可以被访问
  • 大致思路

    • 从对象分配处出发,沿着控制流,观察数据流。若发现指针 p 在当前作用域 s:

      • 作为参数传递给其他函数;
      • 传递给全局变量;
      • 传递给其他的 goroutine;
      • 传递给已逃逸的指针指向的对象;
    • 则指针 p 逃逸出 s,反之则没有逃逸出 s.

  • 优化:未逃逸出当前函数的指针指向的对象可以在栈上分配

    • 对象在栈上分配和回收很快:移动 sp 即可完成内存的分配和回收;
    • 减少在堆上分配对象,降低 GC 负担。

静态分析

不执行程序代码,推导程序的行为

  • 过程内分析

仅在函数内部进行分析

  • 过程间分析

考虑函数调用时参数传递和返回值的数据流和控制流

总结

今天的这些内存优化和编译器优化啥的,对我来说,感觉好抽象啊,全是概念性的。听的半懵半懂的。看来还需要更努力啊。T.T