我写了一个简单的基准测试,以便找出当通过按位和数组计算数组时是否可以消除边界检查.这几乎就是所有哈希表的作用:它们计算
h & (table.length - 1)
Run Code Online (Sandbox Code Playgroud)
作为索引table,其中h是hashCode或派生值.该结果表明,边界检查不被淘汰.
我的基准的想法很简单:计算两个值i和j,其中既保证是有效的数组索引.
i是循环计数器.当它被用作数组索引时,边界检查被消除.j计算为x & (table.length - 1),x每次迭代时某些值都在变化.当它被用作数组索引时,边界检查不会被消除.相关部分如下:
for (int i=0; i<=table.length-1; ++i) {
x += result;
final int j = x & (table.length-1);
result ^= i + table[j];
}
Run Code Online (Sandbox Code Playgroud)
另一个实验使用
result ^= table[i] + j;
Run Code Online (Sandbox Code Playgroud)
代替.时间上的差异可能是15%(在我尝试的不同变体中非常一致).我的问题:
j?MarkoTopolnik的回答表明它更复杂,并且不能保证取消边界检查是一种胜利,特别是在他的计算机上,"正常"代码比"蒙面"慢.我想这是因为它允许一些额外的优化,在这种情况下显示实际上是有害的(鉴于当前CPU的复杂性,编译器甚至几乎不知道).
leventov的答案清楚地表明,数组边界检查是在"屏蔽"中完成的,并且它的消除使得代码与"正常"一样快.
Donal Fellows指出这样一个事实,即屏蔽不适用于零长度表,x & (0-1)等于x.因此,编译器可以做的最好的事情是用零长度检查替换绑定的检查.但这是恕我直言仍然值得,因为零长度检查可以轻松地移出循环.