GoLang之编译器和静态分析、Go 编译器优化

1.编译器和静态分析

1.1编译器的结构

在这里插入图片描述

1.2静态分析

不执行代码,推导程序的行为,分析程序的性质。

1.3控制流

控制流:程序的执行流程

1.4数据流

数据流:数据在控制流上的传递

在这里插入图片描述

上图的程序转换成控制流图 (control-flow graph)

在这里插入图片描述

1.5Intra-procedural analysis、Inter-procedural analysis

通过分析控制流和数据流,我们可以知道更多关于程序的性质(properties) ,这些事实可以帮助我们做编译优化。

例如上面的程序。我们通过分析数据流和控制流,知道这个程序始终返回 4。编译器可以根据这个结果做出优化。

在这里插入图片描述

Intra-procedural analysis: 函数内分析:在函数内进行控制流和数据流的分析
Inter-procedural analysis: 函数间分析:除了函数内的分析,还需要考虑跨函数的数据流和控制流,例如参数传递,函数返回值等

2.Go 编译器优化

2.1目的

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

2.2现状

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

2.3思路

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

2.4函数内联

2.4.1定义

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

2.4.2优点

优点:
1.消除调用开销
2.将过程间分析的问题转换为过程内分析,帮助其他分析

2.4.3缺点

缺点:
1.函数体变大
2.编译生成的 Go 镜像文件变大

2.4.4特点

函数内联在大多数情况下是正向优化,即多内联,会提升性能

2.4.5采取一定的策略决定是否内联

采取一定的策略决定是否内联:
1.调用和被调用函数的规模

2.4.6Go 内联的限制

Go 内联的限制:
1.语言特性:interface, defer 等等,限制了内联优化
2.内联策略非常保守

2.4.6字节跳动的优化方案

字节跳动的优化方案:
1.修改了内联策略,让更多函数被内联
2.增加了其他优化的机会:逃逸分析

2.4.7开销

开销:
1.Go 镜像大小略有增加
2.编译时间增加
3.运行时栈扩展开销增加

3.逃逸分析

3.1定义

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

3.2大致思路

从对象分配处出发,沿着控制流,观察数据流。若发现指针 p 在当前作用域 s:
1.作为参数传递给其他函数;
2.传递给全局变量;
3.传递给其他的 goroutine;
4.传递给已逃逸的指针指向的对象;
则指针 p 逃逸出 s,反之则没有逃逸出 s.

3.3优化

优化:未逃逸出当前函数的指针指向的对象可以在栈上分配
1.对象在栈上分配和回收很快:移动 sp 即可完成内存的分配和回收;
2.减少在堆上分配对象,降低 GC 负担。