atl*_*ste 2 .net c# optimization jit
对于我的一个项目,我使用了很多分支。思考:
if (foo) { do something }
else if (bar) { do something }
else if (bar2) { do something }
... and so on
Run Code Online (Sandbox Code Playgroud)
和
if (foo) { do something }
if (bar) { do something }
if (bar2) { do something }
... and so on
Run Code Online (Sandbox Code Playgroud)
我一直想知道的是,做子表达式和/或逻辑消除来加速它是否有意义。为了完整起见,您可以假设所有这些都在一个函数中。比如说,如果foo和bar有一个共同的子表达式,你可以这样写:
if (commonSubExpr)
{
if (foo without commonSubExpr) { do something }
if (bar without commonSubExpr) { do something }
}
if (bar2) { do something }
... and so on
Run Code Online (Sandbox Code Playgroud)
同样,您可以应用许多简单的布尔逻辑规则来优化规则。
我的问题是:这样做有意义吗?或者我可以期望 JIT'ter 处理这个问题吗?
(根据Eric Lippert的优秀文章,除了常量折叠之外,编译器没有对此进行优化 - 我认为情况仍然如此)。
更新+1
好吧,我不应该问它是否有意义,因为现在我得到了好人试图向我解释什么是过早优化,这不是我所追求的......我的错误。假设我知道过度设计、过早优化等。请——这正是我在这里试图避免的。
所以尝试 2 ......我想知道事情是如何工作的以及我可以从编译器/ JIT'ter 中得到什么。
我还注意到一些上下文可能会有所帮助,所以这里有一些关于应用程序的信息:
在这种情况下,应用程序是一种域特定语言,它在运行时使用 Reflection.Emit 编译到 IL。我不能使用现有语言或现有编译器是有充分理由的。性能至关重要,编译后会执行许多操作(这就是为什么首先将其编译为 IL 而不是简单地解释代码)。
我问这个的原因是因为我想知道我应该在编译器中设计优化器的范围。如果 JIT'ter 负责消除子表达式,我会将优化器设计为仅执行诸如常量折叠之类的基本操作,如果 .NET 期望这在编译器中发生,我将在编译器中设计它。根据我的预期,优化器将具有完全不同的设计。由于分支可能是最重要的性能消耗者,并且因为实现对我的软件设计有巨大影响,所以我特别决定询问这个问题。
我不知道在实现编译器之前没有办法测试它——这是相当多的工作——所以我想在开始实现之前直接了解我的基础知识。我不知道如何测试的原因是因为我不知道在什么情况下 JIT'ter 优化了哪些代码段;我希望 .NET 运行时中的某些触发器会导致某些优化(使测试结果不可靠)...如果您知道解决此问题的方法,请告诉我。
表达式foo、bar等可以是您通常在代码中看到的任何形式,但您可以假设它是单个函数。因此,它可以是 形式if (StartDate < EndDate),但不能是类似if (method1() < method2()). 解释一下:在后一种情况下,编译器不能简单地对方法的返回值做出假设(您需要在优化此之前获得有关返回值的信息),因此子表达式消除根本不是微不足道的。
因此,作为子表达式消除的示例:
if (int1 < int2 && int1 < int3) {
//...
}
else if (int1 < int2 && int1 < int3) {
//...
}
Run Code Online (Sandbox Code Playgroud)
可以改写为:
if (int1 < int2)
{
if (int1 < int3) {
//...
}
else if (int1 < int3) {
//...
}
}
Run Code Online (Sandbox Code Playgroud)
所以总结一下:我想知道的是这些类型的子表达式消除优化是否有意义 - 或者它们是否由 JIT'ter 处理。
所以,它可以是 if (StartDate < EndDate)
不,不能。您的编译器需要生成对该DateTime.op_LessThan()方法的调用。生成方法调用的麻烦在于您无法 100% 确定该方法不会有可观察到的副作用。DateTime.op_LessThan 没有,但这不是您的编译器可以自行发现的。您必须在编译器中对该规则进行硬编码。
抖动可以但是,它不知道该方法看起来像代码。而且它非常小,它将方法内联到单个 CPU 指令中。平均在不到一个 CPU 周期内执行。处理器中内置的分支预测逻辑可确保分支不会使流水线停滞。
很难让编译器中的优化器获得回报。对于非常简单的代码,它只能消除常见的子表达式而没有副作用,但是这样的代码已经运行得非常快了。C# 编译器是一个很好的模型,它不会优化,而是让工作处于抖动状态。此答案中描述了抖动执行的优化。是的,常见的子表达式消除就是它知道如何执行的一种优化。
然而,它是否应用优化是不可预测的,这取决于它需要在方法中生成哪些其他代码。我还没有检查过这个特定的案例,我很怀疑它会因为分支。如果您还想为 && 和 || 提供短路评估,还有更多的东西。运营商。您可以找到的唯一方法是查看实际生成的机器代码。选择您要使用平台目标设置验证的抖动。构建测试代码的发布配置。和Tools + Options,Debugging,General,把“Suppress JIT optimization”选项去掉。并查看带有断点和Debug + Windows + Disassembly的机器码。注意虚假的测试代码,如果抖动可以优化太多,它通常会以不切实际的速度运行。