简体   繁体   English

Java for循环中的分支预测

[英]Branch prediction in a java for loop

I saw this comment next to a if condition: 我在if条件旁边看到了此评论:

// branch prediction favors most often used condition

in the source code of the JavaFX SkinBase class. JavaFX SkinBase类的源代码中。

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;
}

I believe that the developer want to explain why he wrote a negate if . 我相信开发人员想解释为什么他写了一个否定if

is this optimization really useful ? 这种优化真的有用吗?

The guys of the JavaFX team are pretty focused on performance so they surely know that switching the if and the else has no material effect on branch prediction. JavaFX团队的成员非常专注于性能,因此他们肯定知道切换if和else对分支预测没有实质性影响。

I imagine that it is an indication that there is no significant performance improvement to refactor as: 我认为这表明重构没有显着的性能改进,因为:

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));
  }
}

Because branch prediction will essentially turn the if/else into something like that for you (give or take). 因为分支预测实际上会将if / else变成类似的东西(给定或接受)。


This commit confirms this explanation, although I'm not sure why they changed it - possibly because it had side effects when the isManaged condition was never true... 这个提交确认了这个解释,尽管我不确定为什么他们要更改它-可能是因为当isManaged条件从不成立时它具有副作用。

Investigating further, the commit refers to a bug "up to 50% performance regression in Controls benchmarks" (you need to login to access it). 进一步调查,该提交涉及一个错误“在Controls基准测试中性能下降高达50%” (您需要登录才能访问它)。

It seems that they had a performance issue and while investigating noticed that there was a bug in the code (that was similar to what my example above). 似乎他们遇到了性能问题,在调查时注意到代码中存在错误(与我上面的示例类似)。 They fixed the bug and added a comment to clarify that the fix did not affect performance thanks to branch prediction. 他们修复了该错误,并添加了一条注释以阐明该修复由于分支预测而不会影响性能。

Excerpts: 摘录:

[..] looking at the code, I noticed an error in the case where none of the skin's children are managed. [..]在查看代码时,在没有管理任何皮肤孩子的情况下,我注意到一个错误。 In such a case, minX/Y and maxX/Y would end up being MAX/MIN_VALUE where we really want them to be zero. 在这种情况下,minX / Y和maxX / Y最终将是MAX / MIN_VALUE,我们真正希望它们为零。 So this change set fixes that issue. 因此,此更改集解决了该问题。 In testing, I saw a small (~1 fps) improvement in performance, so I don't think this change fixes the performance problem. 在测试中,我发现性能略有提高(〜1 fps),因此我认为此更改不能解决性能问题。 Regardless, the code must be the way it is. 无论如何,代码必须保持原样。

[...] Note that there was a bug in the use of MIN/MAX - those values are not the largest and smallest values for Float and Double (that would have made sense wrt symmetry with the integral types, but it isn't the way they were specified). [...]请注意,使用MIN / MAX时有一个错误-那些值不是Float和Double的最大值和最小值(与整数类型对称是有意义的,但不是)指定方式)。 For doing min/max operations on floating point values you want to use the NEGATIVE/POSITIVE_INFINITY values instead to achieve the results you are looking for. 为了对浮点值执行最小/最大运算,您想使用NEGATIVE / POSITIVE_INFINITY值代替以获得所需的结果。

The sentence “branch prediction favors most often used condition” doesn't say anything about the value of the evaluated condition, be it positive or negative. “分支预测偏爱最常用的条件”这句话并没有说明评估条件的值,无论是肯定的还是否定的。 It only says that the branch prediction may help the conditional branches that are used more often, ie the ones in a loop. 它只是说分支预测可以帮助更频繁使用的条件分支,即循环中的那些。 So it basically says, using if inside a loop is ok. 所以它基本上说,在循环内使用if是可以的。

While the conclusion is correct, you shouldn't worry about if s in a loop (you shouldn't worry about anything unless a profiler tells you that there is a bottleneck), the sentence itself is pretty meaningless in the context of Java. 虽然结论是正确的,但您不必担心s if循环(除非分析器告诉您存在瓶颈,否则您无需担心任何事情),在Java上下文中,该语句本身是毫无意义的。

Branch prediction is a CPU feature, thus in interpreted execution it's irrelevant to Java level branches as they just modify the interpreter's state (ie the pointer though which the next instruction will be read) but are not associated with a CPU instruction that would be specific to the particular branch. 分支预测是CPU的功能,因此在解释执行中,它与Java级别分支无关,因为它们只是修改解释器的状态(即,将通过其读取下一条指令的指针),但与特定于CPU的指令无关。特定的分支。

Once HotSpot kicks in, the picture is entirely different. HotSpot启动后,情况将完全不同。 If this code is a hot spot, the optimizer will apply plenty of code transformations which render most assumption about, how the Java code will be executed, wrong. 如果此代码是热点,那么优化器将应用大量代码转换,从而使大多数关于Java代码将如何执行的假设都是错误的。

One common optimization is loop unrolling . 一种常见的优化是循环展开 Instead of having one block of code representing the loop's body, there will be multiple instances of it following each other, being optimized regarding invariants of their particular iteration. 与其有一个代表循环主体的代码块,不如说有一个循环的多个实例相互围绕,并对其特定迭代的不变量进行了优化。 This setups allows to elide the if related branches completely as it is perfectly predictable that after the first transition of firstManagedChild from true to false , it will never go back, hence while the first iteration invariably see a true value for it, the code for the subsequent iterations can be optimized to treat the variable as being constantly false . 此设置允许完全消除if相关分支,因为完全可以预测,在firstManagedChildtruefalse的第一次转换之后,它将永远不会返回,因此,尽管第一次迭代始终看到它的true值,但是可以优化后续迭代,以将变量始终视为false

So in that case, branch prediction will again have no meaning as there will be no branches for an if statement whose outcome can be predicted in advance. 因此,在那种情况下,分支预测将再也没有意义,因为if语句的分支可以预先预测,因此没有分支。

An pretty detailled article on branch prediction cab be found here 这里可以找到有关分支预测出租车的详细文章

To answer your question - From my understanding, no. 回答您的问题-根据我的理解,不可以。 I don't think negating the "if" will make any difference whatsoever. 我认为否定“ if”不会有任何影响。 It'll optimise the condition for repeating false values just the same as it would for multiple true values. 它将优化重复假值的条件,就像重复多个真值一样。

In addition to the existing answers: 除了现有的答案:

This comment does not seem to be a justification for the "negate if" (as this should not make a difference performance-wise anyhow). 该评论似乎不是“否定如果”的理由(因为无论如何在性能方面都不会有所不同)。 Instead, it is probably a justification for not trying to "optimize" the code and avoid the single if . 相反,这可能是尝试“优化”代码并避免使用单个if的理由。 This would be possible with something like this: 可以通过以下方式实现:

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;
}

The comment indicates that this will not bring any performance benefit due to the saved if , because this if will in practice essentially be free due to branch prediction. 注释表明,由于保存了if ,这不会带来任何性能上的好处,因为实际上,由于分支预测,该if实际上将是免费的。

Side note: I think that there could be other "microoptimizations", like avoiding Math#min(double,double) . 旁注:我认为可能还有其他“微优化”,例如避免使用Math#min(double,double) But the JavaFX guys (hopefully) know their stuff, and probably would have done this if it was likely to make a difference in their case) 但是JavaFX的家伙(希望)知道他们的知识,并且如果情况可能有所不同,可能会这样做)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM