java非_Java逻辑运算中非运算( ! )和双等号( == )的执行效率谁更高?

作为Java开发,俺想说的是:

!运算符放在那里就是要让人用的。能写 !cond 的地方写成 cond == false 是多此一举的事情。

但确实有些人习惯这样写,这俺也管不着——反正俺的代码里是坚决不会这样写的。

作为从事JVM的JIT编译器研发的人,俺想说的是:

就算生成的字节码形式不一样,如果一个JVM实现有JIT编译器,而那个JIT编译器不能把题主举例的那两种形式优化到完全一样的话,那么那个JIT编译器也是废了。

分析这种问题切记不要被Java字节码的样子给骗了。在带有JIT编译器的JVM实现中,一段代码的性能好坏跟其Java字节码的形态没啥关系,主要还是看JIT编译后的代码质量如何。

Java字节码的形态只会影响程序在解释器里跑的性能。好的JIT编译器生成的代码的性能跟解释器相比可以轻松拉开2-3x甚至10x的差距。所以在这种环境中比性能不要比解释器的性能。

不过——如果两段Java代码在字节码层面看就已经完全一样了的话,那就不用钻进JIT编译器里看了。题主的例子就是如此。

==========================================

具体看例子:

用JDK 1.7.0_45的javac来编译下面的代码:

public class Test {

public static int foo(boolean cond) {

if (!cond) return 123;

return 456;

}

public static int bar(boolean cond) {

if (cond == false) return 123;

return 456;

}

public static int baz(boolean cond) {

if (false == cond) return 123;

return 456;

}

}会得到这样的字节码:

public static int foo(boolean);

Code:

stack=1, locals=1, args_size=1

0: iload_0

1: ifne 7

4: bipush 123

6: ireturn

7: sipush 456

10: ireturn

LineNumberTable:

line 3: 0

line 4: 7

public static int bar(boolean);

Code:

stack=1, locals=1, args_size=1

0: iload_0

1: ifne 7

4: bipush 123

6: ireturn

7: sipush 456

10: ireturn

LineNumberTable:

line 8: 0

line 9: 7foo()和bar()的内容是不是完全一样?问题解决。

不过如果我们看baz()版,也就是把bar()里的实现改为 if (false == cond) 的话,javac就会有点犯傻:

public static int baz(boolean);

Code:

stack=2, locals=1, args_size=1

0: iconst_0 // load constant false

1: iload_0 // load argument 0

2: if_icmpne 8 // are they equal?

5: bipush 123 // yes, load constant 123

7: ireturn // and return it

8: sipush 456 // no, load constant 456

11: ireturn // and return it

LineNumberTable:

line 13: 0

line 14: 8此时不要担心,JIT编译器还是会把它优化到跟前面的写法一样的效果的。

用Oracle JDK 1.7.0_45的Client Compiler(C1)来演示给题主看:

foo():

# {method} 'foo' '(Z)I' in 'Test'

# parm0: rsi = boolean

# [sp+0x40] (sp of caller)

0x000000010eb43220: mov %eax,-0x14000(%rsp)

0x000000010eb43227: push %rbp

0x000000010eb43228: sub $0x30,%rsp ;*iload_0

; - Test::foo@0 (line 3)

0x000000010eb4322c: cmp $0x0,%esi

0x000000010eb4322f: jne 0x000000010eb43246 ;*ifne

; - Test::foo@1 (line 3)

0x000000010eb43235: mov $0x7b,%eax

0x000000010eb4323a: add $0x30,%rsp

0x000000010eb4323e: pop %rbp

0x000000010eb4323f: test %eax,-0x1375145(%rip) # 0x000000010d7ce100

; {poll_return}

0x000000010eb43245: retq ;*ireturn

; - Test::foo@6 (line 3)bar():

# {method} 'bar' '(Z)I' in 'Test'

# parm0: rsi = boolean

# [sp+0x40] (sp of caller)

0x000000010eb43560: mov %eax,-0x14000(%rsp)

0x000000010eb43567: push %rbp

0x000000010eb43568: sub $0x30,%rsp ;*iload_0

; - Test::bar@0 (line 8)

0x000000010eb4356c: cmp $0x0,%esi

0x000000010eb4356f: jne 0x000000010eb43586 ;*ifne

; - Test::bar@1 (line 8)

0x000000010eb43575: mov $0x7b,%eax

0x000000010eb4357a: add $0x30,%rsp

0x000000010eb4357e: pop %rbp

0x000000010eb4357f: test %eax,-0x1375485(%rip) # 0x000000010d7ce100

; {poll_return}

0x000000010eb43585: retq ;*ireturn

; - Test::bar@6 (line 8)baz():

# {method} 'baz' '(Z)I' in 'Test'

# parm0: rsi = boolean

# [sp+0x40] (sp of caller)

0x000000010eb438a0: mov %eax,-0x14000(%rsp)

0x000000010eb438a7: push %rbp

0x000000010eb438a8: sub $0x30,%rsp ;*iconst_0

; - Test::baz@0 (line 13)

0x000000010eb438ac: cmp $0x0,%esi

0x000000010eb438af: jne 0x000000010eb438c6 ;*if_icmpne

; - Test::baz@2 (line 13)

0x000000010eb438b5: mov $0x7b,%eax

0x000000010eb438ba: add $0x30,%rsp

0x000000010eb438be: pop %rbp

0x000000010eb438bf: test %eax,-0x13757c5(%rip) # 0x000000010d7ce100

; {poll_return}

0x000000010eb438c5: retq ;*ireturn

; - Test::baz@7 (line 13)三个版本内容完全一样对不对?

对C、C++、C#之类的类似的语言而言也是如此:这两种写法在一个有合理优化的编译器处理过后性能肯定是一样的。

如果性能不一样请换编译器 ;-)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值