我在if
条件旁边看到了这条评论:
// branch prediction favors most often used condition
protected double computeMinWidth(double height, double topInset, double rightInset, double bottomInset, double leftInset) {
double minX = 0;
double maxX = 0;
boolean firstManagedChild = true;
for (int i = 0; i < children.size(); i++) {
Node node = children.get(i);
if (node.isManaged()) {
final double x = node.getLayoutBounds().getMinX() + node.getLayoutX();
if (!firstManagedChild) { // branch prediction favors most often used condition
minX = Math.min(minX, x);
maxX = Math.max(maxX, x + node.minWidth(-1));
} else {
minX = x;
maxX = x + node.minWidth(-1);
firstManagedChild = false;
}
}
}
double minWidth = maxX - minX;
return leftInset + minWidth + rightInset;
}
我相信开发人员想解释他为什么写negate if。
这种优化真的很有用吗?
答案 0 :(得分:12)
JavaFX团队的人员非常注重性能,因此他们肯定知道切换if和else对分支预测没有实质性影响。
我认为这表明重构没有显着的性能提升:
double minX = Double.MAX_VALUE;
double maxX = Double.MIN_VALUE;
for (int i = 0; i < children.size(); i++) {
Node node = children.get(i);
if (node.isManaged()) {
final double x = node.getLayoutBounds().getMinX() + node.getLayoutX();
minX = Math.min(minX, x);
maxX = Math.max(maxX, x + node.minWidth(-1));
}
}
因为分支预测基本上会将if / else变为类似的东西(给予或接受)。
This commit确认了这个解释,虽然我不确定他们为什么改变它 - 可能是因为当isManaged
条件永远不真实时它有副作用...
进一步调查,提交引用a bug "up to 50% performance regression in Controls benchmarks"(您需要登录才能访问它)。
似乎他们遇到了性能问题,而且调查时发现代码中存在错误(与我上面的例子类似)。他们修复了这个错误,并添加了一条注释,以澄清该修复程序不会影响性能,这要归功于分支预测。
摘录:
[..]查看代码,我注意到在没有管理皮肤的孩子的情况下出现错误。在这种情况下,minX / Y和maxX / Y最终将为MAX / MIN_VALUE,我们真的希望它们为零。因此,此更改集修复了该问题。在测试中,我看到了一个小的(~1 fps)性能改进,所以我不认为这个改变可以解决性能问题。无论如何,代码必须是它的方式。
[...]请注意,使用MIN / MAX时存在一个错误 - 这些值不是Float和Double的最大值和最小值(这对于整数类型来说是有意义的,但它是不是他们指定的方式)。要对浮点值执行最小/最大操作,您需要使用NEGATIVE / POSITIVE_INFINITY值来实现您要查找的结果。
答案 1 :(得分:4)
句子“分支预测有利于最常用的条件”并未说明评估条件的价值,无论是正面还是负面。它只表示分支预测可以帮助更频繁使用的条件分支,即循环中的条件分支。所以它基本上说,在循环中使用if
是可以的。
虽然结论是正确的,但你不应该担心循环中的if
(除非探查器告诉你存在瓶颈,否则你不应该担心任何事情),这句话本身是毫无意义的在Java的上下文中。
分支预测是一个CPU特性,因此在解释执行中它与Java级分支无关,因为它们只是修改解释器的状态(即指针将被读取的下一条指令),但与CPU指令无关。具体到特定的分支。
一旦HotSpot开始,图片就完全不同了。如果这个代码是一个热点,优化器将应用大量的代码转换,这些代码转换可以最大限度地假设Java代码将如何执行,错误。
一个常见的优化是循环展开。不是让一个代码块表示循环的主体,而是会有多个实例跟随彼此,针对其特定迭代的不变量进行优化。此设置允许完全忽略if
相关分支,因为完全可以预测firstManagedChild
从true
到false
的第一次转换后,它将永远不会返回,因此虽然第一次迭代总是会看到true
值,但可以优化后续迭代的代码,将变量视为常量false
。
因此,在这种情况下,分支预测将再次没有意义,因为if
语句没有分支,其结果可以提前预测。
答案 2 :(得分:3)
可以找到一篇关于分支预测的详细文章here
回答你的问题 - 根据我的理解,不。我不认为否定&#34; if&#34;会有任何不同。它会优化重复假值的条件,就像多个真值一样。
答案 3 :(得分:0)
除现有答案外:
这个评论似乎并不是&#34;否定的理由,如果&#34; (因为无论如何,这不应该在性能上有所不同)。相反,它可能是不尝试&#34;优化&#34;代码并避免使用单if
。这可能是这样的:
protected double computeMinWidth(double height, double topInset,
double rightInset, double bottomInset, double leftInset) {
double minX = 0;
double maxX = 0;
// Initialize minX/maxX with the coordinate of the FIRST managed child
int i = 0;
for (i = 0; i < children.size(); i++) {
Node node = children.get(i);
if (node.isManaged()) {
final double x = node.getLayoutBounds().getMinX() + node.getLayoutX();
minX = x;
maxX = x + node.minWidth(-1);
}
}
// Continue the loop at the last index, updating the
// minX/maxX with the remaining managed children
for (; i < children.size(); i++) {
Node node = children.get(i);
if (node.isManaged()) {
final double x = node.getLayoutBounds().getMinX() + node.getLayoutX();
// Don't have to check whether it's the first managed child here.
// Just do the update.
minX = Math.min(minX, x);
maxX = Math.max(maxX, x + node.minWidth(-1));
}
}
double minWidth = maxX - minX;
return leftInset + minWidth + rightInset;
}
评论表明,由于保存if
,这不会带来任何性能优势,因为此if
实际上由于分支预测而基本上是免费的。
旁注:我认为可能有其他&#34;微优化&#34;,如the docs。但是JavaFX的人(希望)知道他们的东西,如果它可能会对他们的情况产生影响,可能会这样做)