【问题标题】:Why does Java not optimize |= assignments?为什么 Java 不优化 |= 分配?
【发布时间】:2019-03-08 17:53:21
【问题描述】:

t*() 在此示例中始终返回 true,而 f*() 始终返回 false。

假设我们有以下表达式

if ( f1() || t1() || f2() || t2() ){
    // do stuff        
}

如果是这种情况,JVM 会优化执行并且只执行f1()t1(),因为它“理解”无论f2()t2() 产生什么,输入if 语句的要求是满足,因此无需进一步计算。

我正在编写这样的代码:

boolean b = false;
b |= f1(); // A
b |= t1(); // B
b |= f2(); // C
b |= t2(); // D

我的一位同事看到这一点并提到他不确定,但 Java 可能会优化语句 C 和 D,因为从语句 B 开始,b 始终是 true,这可能导致一些问题。

我进行了一些测试,似乎所有测试都正确执行(这是所需的行为),但我仍然想知道 为什么不进行优化?我想他可能是对的,JVM 明白一旦b 为真,对它的任何|= 操作都不会改变它的值。

【问题讨论】:

  • 对于它的价值,如果编译器执行了您建议的操作,我会惊讶。如果我看到你描述的代码,我当然不会期望你这样做。
  • 一个问题可能是,运算符|= 不是布尔值或(||),而是二进制或(|)...
  • "如果是这种情况,JVM 会优化执行并且只执行" 这不是在 JVM 中完成的,它是语言的一部分。如果您查看字节码,方法调用之后将跳转到块之后。
  • 我把b |= f2()改成了b = b || f2(),它短路了。
  • @Sun 它确实跳过了对f2() 的评估,但整个语句仍在执行。

标签: java binary-operators conditional-execution


【解决方案1】:

我仍然想知道为什么不进行优化?

因为那将违反 JLS。

声明

b |= f1();

等价于

b = (boolean)(b | f1());

在上面,JLS 要求 b | f1() 的评估如下:

  1. 获取b的值。
  2. 调用f1() 并获取结果值
  3. | 运算符应用于这两个值。

如果btrue1,JLS不允许编译器跳过调用f1()

如果你想要那种语义(短路),你需要使用b = b || f1(); 等等。 (如您所述:b ||= f1() 是语法错误。)


1 - 实际上,在无法观察到(在单线程程序中)f1() 调用发生或未发生的情况下,理论上优化是允许的。但是您只能通过仔细检查 JIT 编译器发出的 本机代码 来检测优化。只有调用完全没有副作用时才会发生这种情况。

【讨论】:

    【解决方案2】:

    调用没有得到优化,因为JLS §15.26.2. Compound Assignment Operators 需要评估右侧的表达式。

    如果左侧操作数表达式不是数组访问表达式,则:

    • 首先,评估左侧操作数以产生 一个变量。如果此评估突然完成,则分配 出于同样的原因,表达式突然完成;右手 不计算操作数,也不发生赋值。
    • 否则,将保存左侧操作数的值,然后计算右侧操作数。

    • ...

    从历史上看,short-circuiting 条件(&&||)而不是按位(&|)运算符的传统至少可以追溯到 C(但可能值得注意的是,C 没有'直到 1999 年才有明确的布尔类型)。

    【讨论】:

      【解决方案3】:

      这更多地是关于布尔运算符和位运算符之间的区别。

      |= 复合运算符是按位运算符,这意味着对这两个项都求值。

      您可以通过设置一个测试轻松调试此问题,其中b 被分配了文字true,然后将|= 分配给返回boolean 值并在其中有一个断点的方法。

      断点总是会触发。

      另一方面,“快捷方式”优化仅适用于布尔运算符:||&&

      注意:关于复合赋值 here 的一些规范,但我在 |= 赋值上找不到相关部分。

      【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2013-05-04
      • 2018-11-01
      • 2013-04-25
      • 2013-05-26
      • 2010-09-12
      • 2021-11-22
      相关资源
      最近更新 更多