我正在用Java写一些代码,在某些时候,程序的流是由两个int变量"a"和"b"非零(注意:a和b从不为负,也从不在整数溢出范围内)。
我可以用
if (a != 0 && b != 0) { /* Some code */ }
或者
if (a*b != 0) { /* Some code */ }
因为我希望这段代码每次运行数百万次,所以我想知道哪一个会更快。我通过在一个巨大的随机生成的数组中比较它们来做实验,我也很好奇数组的稀疏性(fraction of data = 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
快:
我很想知道为什么。谁能给我一点启示?是编译器还是硬件层?
现在我了解了分支预测,我想知道模拟比较将显示什么或 b是非零:
我们确实看到了与预期相同的分支预测效果,有趣的是,图在x轴上有些翻转。
1-我将!(a==0 || b==0)
添加到分析中,看看会发生什么。
2-在学习了分支预测之后,出于好奇,我还加入了a != 0 || b != 0
, (a+b) != 0
和(a|b) != 0
但它们在逻辑上并不等同于其他表达式,因为只有或 b需要非零才能返回true,因此它们不用于比较处理效率。
3-我还添加了我用于分析的实际基准测试,这只是迭代一个任意int变量。
4-有些人建议包含a != 0 & b != 0
而不是a != 0 && b != 0
,并预测它会更接近a*b != 0
,因为我们会删除分支预测效果。我不知道&
可以用于布尔变量,我以为它只用于整数的二进制操作。
注意:在我考虑所有这些的上下文中,int溢出不是一个问题,但在一般上下文中,这肯定是一个重要的考虑因素。
CPU: Intel Core i7-3610QM @ 2.3GHz
Java版本:1.8.0_45
Java(TM) SE运行时环境(build 1.8.0_45-b14)
. exe
Java HotSpot(TM) 64位服务器虚拟机(build 25.45-b02,混合模式)