我正在用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"在超过3%的时间里等于0,那么a*b != 0
比a!=0 && b!=0
快:
我很想知道为什么.有人能解释一下吗?它是编译器还是硬件级的?
Edit: Out of curiosity...现在我了解了分支预测,我想知道模拟比较会显示a OR b是非零的:
我们确实看到了与预期相同的分支预测效果,有趣的是,图形在X轴上有点翻转.
使现代化
1-我在分析中加了!(a==0 || b==0)
,看看会发生什么.
2-在学习了分支预测之后,出于好奇,我还收录了a != 0 || b != 0
、(a+b) != 0
和(a|b) != 0
.但它们在逻辑上并不等同于其他表达式,因为只有ORb需要非零才能返回TRUE,因此它们不是用来比较处理效率的.
3-我还添加了用于分析的实际基准,它只是迭代任意int变量.
4-一些人建议将a != 0 & b != 0
分为a != 0 & b != 0
分,而不是a != 0 && b != 0
分,他们预测它的表现将更接近a*b != 0
分,因为我们将消除分支预测效应.我不知道&
可以用于布尔变量,我以为它只用于整数的二进制运算.
注意:在我考虑所有这些的情况下,int溢出不是一个问题,但在一般情况下,这绝对是一个重要的考虑因素.
CPU:英特尔酷睿i7-3610QM@2.3 GHz
Java version: 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)