我正在用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 -我也包括在内a != 0 || b != 0
,(a+b) != 0
并(a|b) != 0
出于好奇,了解分支预测之后。但是它们在逻辑上不等同于其他表达式,因为只有OR b需要为非零才能返回true,因此它们并不意味着要进行处理效率比较。
3-我还添加了用于分析的实际基准,它只是对任意int变量进行迭代。
4-有人建议使用a != 0 & b != 0
,而不是a != 0 && b != 0
,因为它a*b != 0
会消除分支预测的影响,因此预测其行为会更接近。我不知道&
可以将其与布尔变量一起使用,我认为它仅用于整数操作。
注意:在我正在考虑所有这些情况的情况下,int溢出不是问题,但这绝对是一般情况下的重要考虑因素。
CPU:英特尔酷睿i7-3610QM @ 2.3GHz
Java版本:1.8.0_45
Java™SE运行时环境(内部版本1.8.0_45-b14)
Java HotSpot™64位服务器VM(内部版本25.45-b02,混合模式)
a != 0 & b != 0
。
a*b!=0
少了一个分支
(1<<16) * (1<<16) == 0
但两者都不为零。
a*b
是零,如果一个的a
和b
为零; a|b
仅当两者都为时为零。
if (!(a == 0 || b == 0))
呢 众所周知,微基准测试是不可靠的,这不太可能真正测量到(〜3%听起来对我来说是误差范围)。