为什么 Java 中的 (a*b != 0) 比 (a != 0 && b != 0) 快?
我正在用Java编写一些代码,在某些时候,程序的流取决于两个int变量“a”和“b”是否不为零(注意:a和b永远不会为负,也永远不会在整数溢出范围内)。
我可以用
if (a != 0 && b != 0) { /* Some code */ }
或者
if (a*b != 0) { /* Some code */ }
因为我期望这段代码每次运行数百万次,所以我想知道哪一个会更快。我通过在一个巨大的随机生成的数组上比较它们来做实验,我也很好奇数组的稀疏性(数据分数= 0)将如何影响结果:
long time;
final int len = 50000000;
int arbitrary = 0;
int[][] nums = new int[2][len];
for (double fraction = 0 ; fraction <= 0.9 ; fraction += 0.0078125) {
for(int i = 0 ; i < 2 ; i++) {
for(int j = 0 ; j < len ; j++) {
double random = Math.random();
if(random < fraction) nums[i][j] = 0;
else nums[i][j] = (int) (random*15 + 1);
}
}
time = System.currentTimeMillis();
for(int i = 0 ; i < len ; i++) {
if( /*insert nums[0][i]*nums[1][i]!=0 or nums[0][i]!=0 && nums[1][i]!=0*/ ) arbitrary++;
}
System.out.println(System.currentTimeMillis() - time);
}
结果表明,如果您期望“a”或“b”等于0的时间超过〜3%,则比:a*b != 0
a!=0 && b!=0
我很想知道为什么。谁能透露一些光明?是编译器还是在硬件级别?
编辑:出于好奇......现在我了解了分支预测,我想知道模拟比较会显示什么,因为OR b是非零:
我们确实看到了与预期相同的分支预测效果,有趣的是,图形沿着X轴翻转了一些。
更新
1-我添加到分析中以查看会发生什么。!(a==0 || b==0)
2-我也包括,出于好奇,在了解了分支预测之后。但它们在逻辑上并不等价于其他表达式,因为只有 OR b 需要为非零才能返回 true,因此不应比较它们的处理效率。a != 0 || b != 0
(a+b) != 0
(a|b) != 0
3-我还添加了用于分析的实际基准,它只是迭代任意int变量。
4-有些人建议包括而不是,预测它会表现得更接近,因为我们会删除分支预测效应。我不知道它可以与布尔变量一起使用,我认为它仅用于整数的二进制运算。a != 0 & b != 0
a != 0 && b != 0
a*b != 0
&
注意:在我考虑所有这些的上下文中,int溢出不是问题,但这绝对是一般上下文中的一个重要考虑因素。
中央处理器: 英特尔酷睿 i7-3610QM @ 2.3GHz
Java 版本: 1.8.0_45
Java(TM) SE Runtime Environment (build 1.8.0_45-b14)
Java HotSpot(TM) 64-Bit Server VM (build 25.45-b02, mixed mode)