对于我的一个项目,我使用了很多分支。思考:
if (foo) { do something }
else if (bar) { do something }
else if (bar2) { do something }
... and so on
和
if (foo) { do something }
if (bar) { do something }
if (bar2) { do something }
... and so on
我一直想知道的是,进行子表达式和/或逻辑消除以加快速度是否有意义。为了完整起见,您可以假设所有这些都在一个函数中。比如说,如果foo
和bar
有一个共同的子表达式,你可以这样写:
if (commonSubExpr)
{
if (foo without commonSubExpr) { do something }
if (bar without commonSubExpr) { do something }
}
if (bar2) { do something }
... and so on
同样,您可以应用许多简单的布尔逻辑规则来优化规则。
我的问题是:这样做有意义吗?或者我可以期待 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) {
//...
}
可以改写为:
if (int1 < int2)
{
if (int1 < int3) {
//...
}
else if (int1 < int3) {
//...
}
}
所以总结一下:我想知道的是这些类型的子表达式消除优化是否有意义 - 或者它们是否由 JIT'ter 处理。