JIT 和 AOT
本文内容主要来自于偶然看到的一个 b 站视频:https://www.bilibili.com/video/BV1134y1h7ED?spm_id_from=333.999.0.0
之前就不断的在各种不同的场合看到 JIT 编译,AOT 编译,除了基本概念一直对这二者都是比较模糊的概念。通过视频中的介绍有了更多的理解,简而言之:
- JIT 是动态编译,会有更多地运行时信息,可以做更激进的编译优化,更耗内存
- AOT 是静态编译,更多偏控制型指令的优化,为了保证正确性,编译优化相对比较保守。
编译器和解释器的区别在于,编译和执行过程是否同时进行。
JIT : just in time, 即时编译
AOT: ahead of time, 预先编译
根据定义 AOT 可以理解为将代码预先编译成库,那么很明显在此情况下, AOT 肯定是对代码性能提升有所帮助的,不过这部分的提升主要在简化代码控制型语句等。因为没有运行时信息,所以没办法进行更加激进的优化策略,一般编译器为了保证正确性还是需要保守一点的。
那么不同的语言对于控制型语句所占的时间是不一样的,所以 AOT 具体的效果也是不一定的。
控制型语句就是取指令,条件跳转等;计算型指令就是实际的计算。
JIT 则是会在程序运行的过程中,进行分布统计,寻找热点代码块,并将这块编译,然后修改原先的代码内容,已经编译的区域直接调用编译好的库并返回结果就行了。这里比较重要的就是特化。
func f(x, y) {
while(x > 1) {
if(x % 2 == 1) {
y+=x;
}
x-=1;
}
} // 500 us
以上代码经过 JIT 可能会有如下改变, 将原函数重命名为 : f_old, 并且重新特化其中的一种情况:
func f1(y) {
y+=1;
} // 100 us
而新的函数则是:
fuc f(x, y) {
x == 1 ? f1(y) : f_old(x, y);
} // 50 us
在运行的过程中,假设统计发现,x 为 1 的情况比较多,那么做上述的代码优化,那么大部分的调用整体时间就从 500 us 减到了 150 us, 当然在 x 不为 1 的情况函数运行时间就从 500 us 变成了 550 us。
并且这次运行可能是 x = 1 的情况比较多,下次运行可能就是 x =3 的情况比较多,这里 AOT 的编译方式肯定是没办法预测的,因此没办法做很多的优化。因为 JIT 可以拿到运行时的一些信息,是可以做更加激进的优化的。
所以一般情况下 JIT 的性能都是比 AOT 要好的。当然 JIT 的内存开销也会更大。