JIT 编译器可以做什么 AOT 编译器不能做的事情?
What can a JIT compiler do that an AOT compiler cannot?
即时 (JIT) 编译器可以根据提前 (AOT) 编译器无法获得的运行时信息优化程序。
此运行时信息最明显的例子是目标平台,例如程序 运行 所在的确切 CPU,或可能可用的任何加速器,例如 GPU。这就是 OpenCL 是 JIT 编译的意义。
但假设我们提前知道目标平台是什么:我们知道哪些 SIMD 扩展可用,等等。JIT 编译器可以利用 AOT 编译器不可用的其他哪些运行时信息?
HotSpot 风格的 JIT 编译器会自动优化程序的热点...但是 AOT 编译器不能只优化整个程序、热点和所有吗?
我想要一些 JIT 编译器可以执行而 AOT 编译器不能执行的特定优化的示例。如果您能提供任何证据证明此类优化在 "real world" 场景中的有效性,则加分。
一个优点是 JIT 编译器可以连续分析代码并优化输出,例如 align/unalign 一些代码块,取消优化一些函数,重新排序分支以减少错误预测...
当然,AoT 编译器也可以进行配置文件引导优化,但它们仅限于开发人员和测试人员执行的测试用例,这可能无法反映真实输入的动态特性
例如,当他们在 Kitkat 中引入 ART 时,Android 已经从 AoT-only 回归到 Nougat 和更高版本中的混合方法,其中应用程序的某些部分是提前编译的优化较少,然后在 运行 之后,配置文件结果将用于在 phone 收费时再次优化应用程序
Android 7.0 Nougat introduced JIT compiler with code profiling to ART, which lets it constantly improve the performance of Android apps as they run. The JIT compiler complements ART's current Ahead of Time compiler and helps improve runtime performance.[9]
一些相关问题:
- JIT compiler vs offline compilers
- when is java faster than c++ (or when is JIT faster then precompiled)?
What can a JIT compiler do that an AOT compiler cannot?
理论上;没什么,因为 AOT 编译器可以在生成的代码中插入 JIT 编译器(and/or 可以生成自修改代码,生成 123 个替代版本和 select 根据 [= 使用哪个版本26=]-时间信息,...).
实践中; AOT 编译器受限于编译器设计者想要处理的复杂程度、编译的语言以及编译器的使用方式。例如,某些编译器(英特尔的 ICC)将生成代码的多个版本,并且(在 运行 时间)根据 CPU 正在 运行 上决定使用哪个版本,但大多数编译器不是为这样做而设计的;许多语言不提供任何方法来控制 "locality"(并减少 TLB 未命中和缓存未命中的机会);并且通常编译器的使用方式会产生阻碍优化的障碍(例如,单独的 "compilation units"/object 文件稍后链接在一起,可能包括动态链接,AOT 编译器不可能进行整个程序优化和只能孤立地优化零件)。所有这些都是实现细节,而不是 AOT 的限制。
换句话说;实际上 "AOT vs. JIT" 是实现的比较,而不是 "AOT vs. JIT" 本身的真正比较;在实践中,由于实现细节,AOT 的性能很差,而 JIT 的性能比差的性能要差得多,因为 JIT 本身很糟糕(昂贵的优化根本不可行,因为它们是在 运行 时间完成的); JIT 看起来 "almost as good" 的唯一原因是它只是 "almost as good as bad".
JIT 可以根据 运行 时间信息进行优化,这会导致在编译时无法证明的更严格的边界条件。示例:
- 可以看出内存位置没有别名(因为采用的代码路径从未对其进行别名),因此将变量保存在寄存器中;
- 它可以消除对永远不会发生的条件的测试(例如基于参数的当前值);
- 它可以访问完整的程序并且可以在它认为合适的地方内联代码;
- 它可以在运行时间根据特定的使用模式进行分支预测,从而达到最优。
内联主要也对 link 现代 compilers/linkers 的时间优化开放,但如果为了以防万一在整个代码中应用,可能会导致令人望而却步的代码膨胀;在 运行 的时候,它可以只在需要的地方应用。
如果程序编译两次,中间有一个测试运行,则可以使用普通编译器改进分支预测;首先 运行 对代码进行检测,以便生成用于生产编译的分析数据 运行 优化分支预测。如果测试 运行 不典型(并且生成典型测试数据并不总是那么容易,或者使用模式可能会在程序的生命周期内发生变化),则预测也不是最佳的。
此外,link time 和 运行 time 静态编译数据优化都需要在构建过程中付出大量努力(在某种程度上,我还没有看到它们在 10 或所以我一生中工作过的地方);对于 JIT,它们默认处于启用状态。
即时 (JIT) 编译器可以根据提前 (AOT) 编译器无法获得的运行时信息优化程序。
此运行时信息最明显的例子是目标平台,例如程序 运行 所在的确切 CPU,或可能可用的任何加速器,例如 GPU。这就是 OpenCL 是 JIT 编译的意义。
但假设我们提前知道目标平台是什么:我们知道哪些 SIMD 扩展可用,等等。JIT 编译器可以利用 AOT 编译器不可用的其他哪些运行时信息?
HotSpot 风格的 JIT 编译器会自动优化程序的热点...但是 AOT 编译器不能只优化整个程序、热点和所有吗?
我想要一些 JIT 编译器可以执行而 AOT 编译器不能执行的特定优化的示例。如果您能提供任何证据证明此类优化在 "real world" 场景中的有效性,则加分。
一个优点是 JIT 编译器可以连续分析代码并优化输出,例如 align/unalign 一些代码块,取消优化一些函数,重新排序分支以减少错误预测...
当然,AoT 编译器也可以进行配置文件引导优化,但它们仅限于开发人员和测试人员执行的测试用例,这可能无法反映真实输入的动态特性
例如,当他们在 Kitkat 中引入 ART 时,Android 已经从 AoT-only 回归到 Nougat 和更高版本中的混合方法,其中应用程序的某些部分是提前编译的优化较少,然后在 运行 之后,配置文件结果将用于在 phone 收费时再次优化应用程序
Android 7.0 Nougat introduced JIT compiler with code profiling to ART, which lets it constantly improve the performance of Android apps as they run. The JIT compiler complements ART's current Ahead of Time compiler and helps improve runtime performance.[9]
一些相关问题:
- JIT compiler vs offline compilers
- when is java faster than c++ (or when is JIT faster then precompiled)?
What can a JIT compiler do that an AOT compiler cannot?
理论上;没什么,因为 AOT 编译器可以在生成的代码中插入 JIT 编译器(and/or 可以生成自修改代码,生成 123 个替代版本和 select 根据 [= 使用哪个版本26=]-时间信息,...).
实践中; AOT 编译器受限于编译器设计者想要处理的复杂程度、编译的语言以及编译器的使用方式。例如,某些编译器(英特尔的 ICC)将生成代码的多个版本,并且(在 运行 时间)根据 CPU 正在 运行 上决定使用哪个版本,但大多数编译器不是为这样做而设计的;许多语言不提供任何方法来控制 "locality"(并减少 TLB 未命中和缓存未命中的机会);并且通常编译器的使用方式会产生阻碍优化的障碍(例如,单独的 "compilation units"/object 文件稍后链接在一起,可能包括动态链接,AOT 编译器不可能进行整个程序优化和只能孤立地优化零件)。所有这些都是实现细节,而不是 AOT 的限制。
换句话说;实际上 "AOT vs. JIT" 是实现的比较,而不是 "AOT vs. JIT" 本身的真正比较;在实践中,由于实现细节,AOT 的性能很差,而 JIT 的性能比差的性能要差得多,因为 JIT 本身很糟糕(昂贵的优化根本不可行,因为它们是在 运行 时间完成的); JIT 看起来 "almost as good" 的唯一原因是它只是 "almost as good as bad".
JIT 可以根据 运行 时间信息进行优化,这会导致在编译时无法证明的更严格的边界条件。示例:
- 可以看出内存位置没有别名(因为采用的代码路径从未对其进行别名),因此将变量保存在寄存器中;
- 它可以消除对永远不会发生的条件的测试(例如基于参数的当前值);
- 它可以访问完整的程序并且可以在它认为合适的地方内联代码;
- 它可以在运行时间根据特定的使用模式进行分支预测,从而达到最优。
内联主要也对 link 现代 compilers/linkers 的时间优化开放,但如果为了以防万一在整个代码中应用,可能会导致令人望而却步的代码膨胀;在 运行 的时候,它可以只在需要的地方应用。
如果程序编译两次,中间有一个测试运行,则可以使用普通编译器改进分支预测;首先 运行 对代码进行检测,以便生成用于生产编译的分析数据 运行 优化分支预测。如果测试 运行 不典型(并且生成典型测试数据并不总是那么容易,或者使用模式可能会在程序的生命周期内发生变化),则预测也不是最佳的。
此外,link time 和 运行 time 静态编译数据优化都需要在构建过程中付出大量努力(在某种程度上,我还没有看到它们在 10 或所以我一生中工作过的地方);对于 JIT,它们默认处于启用状态。