为什么这个带有几个“或”语句的代码比在Java中使用查找表略快?

在查看我昨天( 这里 )提到的微优化问题时,我发现了一些奇怪的东西:Java中的一个or一个语句的运行速度比在一个布尔数组中查找一个布尔值要快一些。

在我的测试中,运行以下算法的long值从0到10亿,alg1大约快2%。 (我已经改变了算法测试的顺序,我得到了相同的结果)。 我的问题是: 为什么alg1更快? 我原本期望alg2稍快一些,因为它使用查找表,而alg1必须执行4次比较,3次或75%的输入操作。

 private final static boolean alg1(long n) { int h = (int)(n & 0xF); if(h == 0 || h == 1 || h == 4 || h == 9) { long tst = (long)Math.sqrt(n); return tst*tst == n; } return false; } private final static boolean[] lookup = new boolean[16]; static { lookup[0] = lookup[1] = lookup[4] = lookup[9] = true; } private final static boolean alg2(long n) { if(lookup[(int)(n & 0xF)]) { long tst = (long)Math.sqrt(n); return tst*tst == n; } else return false; } 

如果你很好奇,这段代码会测试一个数字是否是一个完美的正方形,并利用完美正方形必须以hex表示0,1,4或9的事实。

加载一些随机数据通常比一些非分支代码慢。

当然,这完全取决于处理器架构。 您的第一个if语句可以实现为四个指令。 第二个可能需要空指针检查,边界检查以及加载和比较。 此外,更多代码意味着更多的编译时间,并且以某种方式阻止优化的机会更多。

猜想问题是数组的范围检查以及数组查找是否作为方法调用实现。 这肯定会超过4连胜比较。 你看过字节码了吗?

根据这篇文章,访问数组元素是“访问非数组元素的2到3倍”。 您的测试表明差异可能更大。

在当前的例子中,我同意边界检查可能是你得到的(为什么JVM没有优化它超出我的范围 – 示例代码可以确定地显示为不溢出…

另一种可能性(特别是对于更大的查找表)是缓存延迟…它取决于处理器寄存器的大小以及JVM如何选择使用它们 – 但如果字节数组不完全保留在处理器上,那么你’与简单的OR相比,会看到性能命中,因为每次检查都会将数组拉到CPU上。

这是一段有趣的代码,但2%是一个非常小的差异。 我认为你不能从中得出很多结论。