JIT编译器的IL优化

13

我正在开发一个编译器,用于生成IL代码。 重要的是,最终生成的IL代码能够被Mono和Microsoft .NET JIT编译器JIT编译成最快的机器码。

我的问题是:

  1. 优化像以下这样的模式是否有意义:

    'stloc.0; ldloc.0; ret' => 'ret' 
    'ldc.i4.0; conv.r8' => 'ldc.r8.0'
    
  2. 那么这些问题是否需要考虑,或者JIT编译器是否足够智能以解决这些问题?

  3. 是否有规范列出了Microsoft/Mono JIT编译器执行的优化列表?

  4. 有没有实用建议/最佳实践的好阅读材料,以优化IL,使得JIT编译器可以生成性能最优的机器码?


1
据我所知,JIT 在消除 stloc.0; ldloc.0; 方面非常出色。对于 IronScheme,我尝试调整输出的 IL 使其更像基于 C# 的代码,因为我感觉 JIT 可能会更努力地优化已知的模式。但这只是一种感觉 :D 您可以随时创建一些微型基准测试来衡量它。 - leppie
1
.NET JIT编译器并不特别聪明(毕竟它们没有太多时间)。你为什么关心“最快可能”呢? - Luaan
1
@Luaan,我关心的是“最快可能”的编译器,因为它需要为密集计算生成代码。理想情况下,它应该生成本机机器代码,但出于更好的可移植性和可维护性考虑,我正在考虑使用IL。然而,性能仍然是首要任务。 - Denis Yarkovoy
@DenisYarkovoy 你可以创建一些微基准来分析结果... - xanatos
如果性能不是很关键的话,我会选择使用IL;如果性能很重要的话,我会选择使用本地代码。可移植性很棘手,但是——YAGNI。只需确保它实际上是安全的 :) - Luaan
2个回答

5
  1. 你所描述的两种模式是JIT实际上轻松解决的问题(除了非原始结构体)。在SSA格式中,常量传播和死代码的消除非常容易。
  2. 不,你必须测试JIT可以做什么。查阅编译器文献以了解预期的标准优化。然后,进行测试。我们现在拥有的两个JIT优化很少,有时甚至不能正确处理最基本的东西。例如,MyStruct s; s.x = 1; s.x = 1;在RyuJIT中不会被优化。s = s;也不会。s.x + s.x会从内存中加载x两次。期望很少。
  3. 你需要了解机器码基本操作映射到什么。这并不太复杂。尝试一些东西并查看反汇编列表。你很快就会对输出的样子有感觉。

为什么SSA很重要?JIT编译器是否在内部使用它? - svick
@svick 我非常确定他们会这样做。这似乎非常重要。CoreCLR中的“ssabuilder.cpp”似乎做了类似的事情。请参阅https://en.wikipedia.org/wiki/Static_single_assignment_form#Compilers_using_SSA_form,看看它有多么普遍。 - usr

5

冗余的转换和类似于装载/存储的操作是递归下降解析器的不可避免的副作用。你可以使用Peephole优化器来去除它们,但这并不需要担心,C# 和 VB.NET 编译器也会生成它们。

现有的 .NET/Mono jitter非常擅长优化这些操作。他们专注于优化真正影响执行速度的机器代码。一个很好的优势是,任何编写生成IL的编译器都可以受益于这些优化而无需进行特殊处理。

Jitter 优化在此帖子中有涉及。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接