我results的benchmark表示,当分支机构的概率为15%(或85%)而不是50%时,性能最差。
有任何解释吗?
代码太长但相关部分在这里:
private int diff(char c) {
return TABLE[(145538857 * c) >>> 27] - c;
}
@Benchmark int timeBranching(int reps) {
int result = 0;
while (reps-->0) {
for (final char c : queries) {
if (diff(c) == 0) {
++result;
}
}
}
return result;
}
它计算给定字符串中BREAKING_WHITESPACE个字符的数量。结果显示当分支概率达到约0.20时突然下降(性能增加)。
关于跌幅的更多details。 Varying the seed显示更多奇怪的事情发生。请注意,表示最小值和最大值的黑线非常短,除非靠近悬崖。
答案 0 :(得分:10)
它看起来像一个小的JIT错误。对于小分支概率,它会产生类似下面的内容,因为展开会更复杂(我简化了很多):
movzwl 0x16(%r8,%r10,2),%r9d
获取字符:int r9d = queries[r10]
imul $0x8acbf29,%r9d,%ebx
乘以:ebx = 145538857 * r9d
shr $0x1b,%ebx
Shift:ebx >>>= 27
cmp %edx,%ebx
jae somewhere
检查边界:if (ebx > edx || ebx < 0) goto somewhere
(然后扔IndexOutOfBoundsException
。
cmp %r9d,%ebx
jne back
如果不相等,则跳回循环开始:if (r9d != ebx) goto back
inc %eax
增加结果:eax++
jne back
只需goto back
我们可以看到一个聪明而且愚蠢的事情:
对于超过20%的分支概率,这三条指令
cmp %r9d,%ebx
jne back
inc %eax
被
之类的东西取代mov %eax,%ecx // ecx = result
inc %ecx // ecx++
cmp %r9d,%ebx // if (r9b == index)
cmoveq %ecx,%ebx // result = ecx
使用conditional move指令。这是一条指令,但没有分支,因此没有分支错误预测惩罚。
这解释了20-80%范围内非常恒定的时间。低于20%的增长显然是由于分支机构的错误预测。
因此看起来JIT未能使用大约0.04而不是0.18的正确阈值。