ase*_*a38 41 java performance micro-optimization
我写了两种方法来检查性能
public class Test1 {
private String value;
public void notNull(){
if( value != null) {
//do something
}
}
public void nullNot(){
if( null != value) {
//do something
}
}
}
Run Code Online (Sandbox Code Playgroud)
并在编译后检查它的字节码
public void notNull();
Code:
Stack=1, Locals=1, Args_size=1
0: aload_0
1: getfield #2; //Field value:Ljava/lang/String;
4: ifnull 7
7: return
LineNumberTable:
line 6: 0
line 9: 7
StackMapTable: number_of_entries = 1
frame_type = 7 /* same */
public void nullNot();
Code:
Stack=2, Locals=1, Args_size=1
0: aconst_null
1: aload_0
2: getfield #2; //Field value:Ljava/lang/String;
5: if_acmpeq 8
8: return
LineNumberTable:
line 12: 0
line 15: 8
StackMapTable: number_of_entries = 1
frame_type = 8 /* same */
}
Run Code Online (Sandbox Code Playgroud)
在这里,两个操作码用于实现if条件:在第一种情况下,它使用ifnull-检查堆栈的最高值为null-,在第二种情况下,它使用if_acmpeq-检查前两个值在堆栈中是相等的 -
那么,这会对性能产生影响吗?(这将有助于我证明null的首次实现在性能方面以及在可读性方面都很好:))
pol*_*nts 76
比较生成的字节码大多没有意义,因为大多数优化都是在运行时使用JIT编译器进行的.我猜想在这种情况下,表达式同样快.如果有任何差异,则可以忽略不计.
这不是你需要担心的事情.寻找大图优化.
Rex*_*err 10
有了这样的问题,很难知道JVM会有多聪明(虽然答案是"如果可能的话通常很聪明",在这种情况下看起来非常可能).但只是为了确定,测试一下:
class Nullcheck {
public static class Fooble { }
Fooble[] foo = {null , new Fooble(), null , null,
new Fooble(), null, null, new Fooble() };
public int testFirst() {
int sum = 0;
for (int i=0 ; i<1000000000 ; i++) if (foo[i&0x7] != null) sum++;
return sum;
}
public int testSecond() {
int sum = 0;
for (int i=0 ; i<1000000000 ; i++) if (null != foo[i&0x7]) sum++;
return sum;
}
public void run() {
long t0 = System.nanoTime();
int s1 = testFirst();
long t1 = System.nanoTime();
int s2 = testSecond();
long t2 = System.nanoTime();
System.out.printf("Difference=%d; %.3f vs. %.3f ns/loop (diff=%.3f)\n",
s2-s1,(t1-t0)*1e-9,(t2-t1)*1e-9,(t0+t2-2*t1)*1e-9);
}
public static void main(String[] args) {
Nullcheck me = new Nullcheck();
for (int i=0 ; i<5 ; i++) me.run();
}
}
Run Code Online (Sandbox Code Playgroud)
在我的机器上产生:
Difference=0; 2.574 vs. 2.583 ns/loop (diff=0.008)
Difference=0; 2.574 vs. 2.573 ns/loop (diff=-0.001)
Difference=0; 1.584 vs. 1.582 ns/loop (diff=-0.003)
Difference=0; 1.582 vs. 1.584 ns/loop (diff=0.002)
Difference=0; 1.582 vs. 1.582 ns/loop (diff=0.000)
Run Code Online (Sandbox Code Playgroud)
所以答案是:不,没有任何有意义的差异.(并且JIT编译器可以在相同次数的重复运行之后找到额外的技巧来加速每个.)
更新:上面的代码运行ad-hoc基准测试.使用JMH(现在它存在!)是帮助避免(某些)微基准测试陷阱的好方法.上面的代码避免了最严重的陷阱,但它没有给出明确的错误估计,而忽略了有时重要的其他各种事情.这些天:使用JMH!此外,如有疑问,请运行自己的基准测试.细节有时很重要 - 对于像这样简单的事情来说并不常见,但如果对你来说非常重要,你应该检查一个尽可能接近生产的条件.
除了在C中避免意外赋值的来之不易的智慧,它有利于将常量放在二元运算符的左边,我发现左边的常量更具可读性,因为它将关键值放在最显着的位置.
通常一个函数体只会使用几个变量,而且通常通过上下文显而易见的是哪个变量正在检查中.通过将常量放在左侧,我们更接近地模仿switch并且case:给定此变量,选择匹配值.看到左侧的值,一个侧重于所选的特定条件.
当我扫描
if (var == null)
Run Code Online (Sandbox Code Playgroud)
我读到它,"我们正在var这里检查,我们正在比较它是否平等,反对......啊,null." 相反,当我扫描
if (null == var)
Run Code Online (Sandbox Code Playgroud)
我想,"我们看到一个值是否为空,并且......是的,var我们正在检查它." 这是一个更强烈的认可
if (null != var)
Run Code Online (Sandbox Code Playgroud)
我的眼睛立即接受了.
这种直觉来自于习惯的一致性,更喜欢阅读所写的内容,以及写出喜欢阅读的内容.人们可以从任何一种方式学习它,但它不是客观真实的,因为其他人在这里回答说将变量放在左边是更清楚的.这取决于首先想要最明确的表达方式.
看到字节码差异很有吸引力.谢谢你的分享.
| 归档时间: |
|
| 查看次数: |
25814 次 |
| 最近记录: |