【问题标题】:Does Java optimize function calls for unnecessary boolean comparisons at runtime?Java 是否会在运行时优化函数调用以进行不必要的布尔比较?
【发布时间】:2017-04-01 13:59:49
【问题描述】:

如果其中一个不返回 true,java 会在运行时调用所有三个函数吗?

通常出于调试目的,我喜欢在调试器中查看这些条件检查的结果,并将它们放在一行中会使这变得更加困难。如果有六次检查,那么代码就会变得更加不可读。

boolean result1 = CheckSomething();
boolean result2 = CheckSomethingElse();
boolean result3 = CheckSomeOtherThing();

boolean finalResult = result1 && result2 && result3;

如果 Java 没有在运行时优化这个条件,那么应该通过编写这样的代码来获得更好的性能,但代价是无法在调试器中轻松查看中间值。

boolean finalResult = CheckSomething() && CheckSomethingElse() && CheckSomeOtherThing();

【问题讨论】:

  • 在你的最后一个例子中,如果 CheckSomething() 返回 false 其他函数将不会被调用
  • @matiaselgart 那将是一个不同的问题。他问的是编译器是否会生成不同的字节码,而不是在布尔运算的结果已知(它不知道)之后评估是否继续。
  • 如果这些函数中的任何一个有副作用,Java 需要在您的第一个代码 sn-p 中执行副作用。
  • @Stefan - 我不认为他只是在谈论不同的 字节码,因为他将其标记为 jit。字节码肯定不一样,但编译出来的代码肯定不一样。

标签: java optimization jit


【解决方案1】:

一般你的代码 sn-ps 是不等价的。第一个版本的行为必须如同所有函数都被调用,因为您在代码中无条件地调用它们。

您的代码的第二个版本:

boolean finalResult = CheckSomething() && CheckSomethingElse() && CheckSomeOtherThing();

不等价,因为&&操作是快捷方式,所以如果有返回false,其余函数将不会被调用。

在解释器中运行时,您可以预期您的代码的第一个版本实际调用所有方法。

现在,有趣的部分:代码编译后,编译器现在可以“查看”各种函数,确定它们是纯函数,没有副作用,并且可以编译两个版本相同。事实上,如果函数非常简单,最好将两个版本编译为始终调用所有三个函数,因为将三个布尔结果组合起来并检查一次可能会更好如果任何结果不可预测,则速度会更快。

编译器可能并不总是能够确定函数是否具有副作用,或者实际上可能存在只有您知道并不重要的副作用(例如,您知道不会发生但编译器可以的 ArrayOutOfBoundsException '不证明)。所以通常最好写第二种形式,它允许编译器使用短路评估,不管它能够证明什么函数。

【讨论】:

  • 如果所有三个方法都没有副作用,编译器甚至可以重新排序它们,首先进行更便宜的检查。或者首先执行最有可能返回false 的检查(以最有可能跳过其他检查)。由于即使在使用短路变体时也是如此,所以建议始终使用短路形式仍然是正确的。
【解决方案2】:

你可以这样写:

boolean result1 = condition1();
boolean result2 = result1 && condition2();
boolean result3 = result2 && condition3();

这允许您逐步检查每个条件,并查看中间结果,而无需执行不必要的代码。

您还可以在result3 行之后设置一个断点,它会显示返回false 的第一个结果。

【讨论】:

  • 或者只是一个 result 变量。
  • @Andreas 这个问题的主要原因是能够查看中间结果。只有一个变量可以防止这种情况发生。
  • 并非如此。当您单步执行 代码时,您会看到中间结果。通常,一旦您越过变量范围的末尾,您就无法查看它是什么。这没有什么不同。一旦跨过下一行,上一行的结果就不再可用。当然,单独的变量会扩展中间值的范围,但重用单个变量并不会阻止您看到它们。
  • 我刚刚意识到您可能误解了我的第一条评论。我并不是要将 3 个语句合并为一个语句,我只是想使用单个变量而不是三个,即 boolean result = condition1(); result = result && condition2(); result = result && condition3();
  • 使用单独的变量允许我只有一个断点,当我停在那里时,我可以查看所有三个值,看看哪个条件是第一个返回false。使用单个变量是不可能的。
【解决方案3】:

同意,第二个示例将继续评估,直到达到 false 条件,然后它将返回“false”。正在使用短路运算符 &&,因此一旦遇到错误条件,评估就会停止。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-12-27
    • 2014-05-17
    • 2020-06-04
    • 2011-11-04
    • 2018-05-02
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多