为什么 Java 不优化 |= 赋值?
Why does Java not optimize |= assignments?
对于此示例,t*()
始终 returns true,而 f*()
始终 returns false。
假设我们有以下表达式
if ( f1() || t1() || f2() || t2() ){
// do stuff
}
如果是这种情况,JVM 会优化执行并只执行 f1()
和 t1()
因为它 'understands' 无论 f2()
和 t2()
yield,满足进入if语句的要求,无需再进行计算
我正在编写一个代码,其中我写了这样的东西:
boolean b = false;
b |= f1(); // A
b |= t1(); // B
b |= f2(); // C
b |= t2(); // D
我的一位同事看到了这个并提到他不确定,但是 Java 可能优化语句 C 和 D,因为 b
总是 true
从声明 B
开始,这可能会导致一些问题。
我进行了一些测试,似乎所有测试都已正确执行(这是期望的行为),但我仍然想知道为什么这没有得到优化? 我想他可能是对的,JVM 明白一旦 b
为真,没有 |=
操作会改变它的值。
这更多是关于布尔运算符和按位运算符之间的区别。
|=
复合运算符是按位运算符,这意味着对这两项进行计算。
您可以通过设置一个测试来轻松调试它,其中 b
被分配文字 true
,然后 |=
分配给 returns 或者 boolean
值,并且其中有一个断点。
断点会一直触发。
另一方面,"shortcut" 优化仅适用于布尔运算符:||
和 &&
。
注意:关于复合赋值 here 的一些规范,但我找不到关于 |=
赋值的相关部分。
调用没有得到优化,因为 JLS §15.26.2. Compound Assignment Operators 需要计算右侧表达式。
If the left-hand operand expression is not an array access expression, then:
- First, the left-hand operand is evaluated to produce
a variable. If this evaluation completes abruptly, then the assignment
expression completes abruptly for the same reason; the right-hand
operand is not evaluated and no assignment occurs.
Otherwise, the value of the left-hand operand is saved and then the right-hand operand is evaluated.
...
从历史上看,short-circuiting 条件运算符(&&
、||
)而非按位运算符(&
、|
)的传统可以追溯到 at至少是 C(但值得注意的是,直到 1999 年,C 才具有明确的布尔类型)。
I'm still wondering why doesn't this get optimized?
因为那样会违反 JLS。
声明
b |= f1();
等同于
b = (boolean)(b | f1());
在上面,JLS 要求 b | f1()
计算如下:
- 获取
b
的值。
- 调用
f1()
并捕获结果值
- 对两个值应用
|
运算符。
如果 b
是 true
1[,JLS 不会 允许编译器跳过调用 f1()
=53=].
如果你想要那种语义(短路),你需要使用b = b || f1();
等等。 (如您所述:b ||= f1()
是语法错误。)
1 - 实际上,在无法观察(在单线程程序中)f1()
调用是否发生的情况下,优化将在理论是允许的。但是您只能通过仔细检查 JIT 编译器发出的 本机代码 来检测优化。它只有在调用完全没有副作用的情况下才会发生。
t*()
始终 returns true,而 f*()
始终 returns false。
假设我们有以下表达式
if ( f1() || t1() || f2() || t2() ){
// do stuff
}
如果是这种情况,JVM 会优化执行并只执行 f1()
和 t1()
因为它 'understands' 无论 f2()
和 t2()
yield,满足进入if语句的要求,无需再进行计算
我正在编写一个代码,其中我写了这样的东西:
boolean b = false;
b |= f1(); // A
b |= t1(); // B
b |= f2(); // C
b |= t2(); // D
我的一位同事看到了这个并提到他不确定,但是 Java 可能优化语句 C 和 D,因为 b
总是 true
从声明 B
开始,这可能会导致一些问题。
我进行了一些测试,似乎所有测试都已正确执行(这是期望的行为),但我仍然想知道为什么这没有得到优化? 我想他可能是对的,JVM 明白一旦 b
为真,没有 |=
操作会改变它的值。
这更多是关于布尔运算符和按位运算符之间的区别。
|=
复合运算符是按位运算符,这意味着对这两项进行计算。
您可以通过设置一个测试来轻松调试它,其中 b
被分配文字 true
,然后 |=
分配给 returns 或者 boolean
值,并且其中有一个断点。
断点会一直触发。
另一方面,"shortcut" 优化仅适用于布尔运算符:||
和 &&
。
注意:关于复合赋值 here 的一些规范,但我找不到关于 |=
赋值的相关部分。
调用没有得到优化,因为 JLS §15.26.2. Compound Assignment Operators 需要计算右侧表达式。
If the left-hand operand expression is not an array access expression, then:
- First, the left-hand operand is evaluated to produce a variable. If this evaluation completes abruptly, then the assignment expression completes abruptly for the same reason; the right-hand operand is not evaluated and no assignment occurs.
Otherwise, the value of the left-hand operand is saved and then the right-hand operand is evaluated.
...
从历史上看,short-circuiting 条件运算符(&&
、||
)而非按位运算符(&
、|
)的传统可以追溯到 at至少是 C(但值得注意的是,直到 1999 年,C 才具有明确的布尔类型)。
I'm still wondering why doesn't this get optimized?
因为那样会违反 JLS。
声明
b |= f1();
等同于
b = (boolean)(b | f1());
在上面,JLS 要求 b | f1()
计算如下:
- 获取
b
的值。 - 调用
f1()
并捕获结果值 - 对两个值应用
|
运算符。
如果 b
是 true
1[,JLS 不会 允许编译器跳过调用 f1()
=53=].
如果你想要那种语义(短路),你需要使用b = b || f1();
等等。 (如您所述:b ||= f1()
是语法错误。)
1 - 实际上,在无法观察(在单线程程序中)f1()
调用是否发生的情况下,优化将在理论是允许的。但是您只能通过仔细检查 JIT 编译器发出的 本机代码 来检测优化。它只有在调用完全没有副作用的情况下才会发生。