这是我参与「第五届青训营 」伴学笔记创作活动的第 7 天
编译器结构
- 结构
- 分析部分 (前端 front end)·
- 词法分析,生成词素 (lexeme)
- 语法分析,生成语法树
- 语义分析,收集类型信息,进行语义检查
- 中间代码生成,生成 intermediate representation (IR)
- 综合部分 (后端 back end)
- 代码优化,机器无关优化,生成优化后的 IR
- 代码生成,生成目标代码
- 分析部分 (前端 front end)·
静态分析
- 静态分析: 不执行程序代码,推导程序的行为,分析程序的性质。
- 控制流 (Control flow): 程序执行的流程
- 数据流 (Data flow): 数据在控制流上的传递
- 通过分析控制流和数据流,我们可以知道更多关于程序的性质 (properties)
- 根据这些性质优化代码
过程间分析和过程内分析
- 过程内分析 (Intra-procedural analysis)
- 仅在函数内部进行分析
- 过程间分析 (lnter-procedural analysis)(比较复杂)
- 考虑函数调用时参数传递和返回值的数据流和控制流
- 例子
- 分析
- 需要通过数据流分析得知变量的具体类型,才能知道变量调用的是哪个类型的函数
- 根据变量的具体类型,产生了新的控制流,变量的函数流,分析继续
- 分析
- 过程间分析需要同时分析控制流和数据流——联合求解,比较复杂
Go 编译器优化
- 为什么做编译器优化
- 用户无感知,重新编译即可获得性能收益
- 通用性优化
- 现状
- 采用的优化少
- 编译时间较短,没有进行较复杂的的代码分析和优化
- 编译优化的思路
- 场景: 面向后端长期执行任务
- Tradeoff: 用编译时问换取更高效的机器码
- Beast mode
-
函数内联
- 内联:
- 将调用位置的代码替换为调用函数的函数体
- 同时对函数体进行重写来绑定参数
- 优点
- 消除函数调用开销, 例如传递参数、保存寄存器等
- 将过程间分析转化为过程内分析,帮助其他优化,例收逃飞鱼分析
- 缺点
- 函数体变大, instruction cache (icache) 不友好
- 开销
- 编译生成的 Go 镜像变大~10%
- 编译时间增加
- Go 函数内联受到的限制较多
- 语言特性限制了函数内联
- 内联策略较为保守
- 内联:
-
逃逸分析
- 分析代码中指针的动态作用域,即指针在何处可以被访问
- 执行思路
- 从对象分配处出发,沿着控制流,观察对象的数据流
- 若发现指针 p 在当前作用域 s
- 作为参数传递给其他函数
- 传递给全局变量
- 传递给其他的 goroutine
- 传递给已逃逸的指针指向的对象
- 则指针 p 指向的对象逃逸出 s,反之则没有逃逸出 s
- 优化:未逃逸的对象可以在栈上分配
- 对象在栈上分配和回收很快: 移动 sp
- 减少在 heap 上的分配,降低 GC 负担
-
默认栈大小调整
-
边界检查消除
-
循环展开
-
...
-