Go 语言逃逸分析(版本1.18.2)

2022-05-15  本文已影响0人  Sun东辉

什么是逃逸分析?

逃逸分析(escape analysis)就是在程序编译阶段根据程序代码中的数据流,对代码中哪些变量需要在栈上分配,哪些变量需要在堆上分配进行静态分析的方法。一个理想的逃逸分析算法自然是能将那些人们认为需要分配在栈上的变量尽可能保留在栈上,尽可能少的“逃逸”到堆上的算法。

原理

两个原则:防止迷途指针。

  1. 栈对象的指针不能被分配到堆上;
  2. 栈对象的指针的生命周期,不能比栈对象的生命周期长;

数据流构建思路:AST树的静态数据流分析(static data-flow analysis)。

  1. 通过 AST 树构造一个有向加权图(权重 = 引用解析次数 - 取地址次数)。
  2. 遍历这个有向加权图,寻找可能违反上述两个原则的赋值路径。如果对象 v 的地址存储在堆中或其他可能比它生命周期长的地方,那么对象 v 就被标记为需要分配到堆上。

数据结构

```go
// An escape holds state specific to a single function being analyzed
// within a batch.
type escape struct {
    *batch

    curfn *ir.Func // function being analyzed

    labels map[*types.Sym]labelState // known labels

    // loopDepth counts the current loop nesting depth within
    // curfn. It increments within each "for" loop and at each
    // label with a corresponding backwards "goto" (i.e.,
    // unstructured loop).
    loopDepth int
}

```

实现

  1. 构造顶点 initFunc
    遍历所有函数的局部变量,检查变量的大小,大对象直接标记。
  2. 构造边 walkFunc
    对目标函数的所有语句递归遍历并进行分析,如果语句中存在赋值关系,则为其创建有向边。
  3. 分析有向加权图
    基于生命周期的判断 outlives
    基于权重的判断 walkOne
  4. 遍历顶点,收集被标记的的逃逸原因 walkAll
    编译器内部通过两个 LIFO Queue 来实现该双重循环。
    在遍历的过程中,会同步分析有向权图,即 for len(todo) > 0 {... b.walkOne(root, walkgen, enqueue)}
上一篇 下一篇

猜你喜欢

热点阅读