Прогноз ветвления в цикле Java для

Я видел этот комментарий рядом с if состояние:

// branch prediction favors most often used condition

в исходном коде 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;
}

Я считаю, что разработчик хочет объяснить, почему он написал отрицание, если.

эта оптимизация действительно полезна?

4 ответа

Решение

Ребята из команды 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 в нечто подобное для вас (дай или возьми).


Этот коммит подтверждает это объяснение, хотя я не уверен, почему они изменили его - возможно, потому что у него были побочные эффекты, когда isManaged состояние никогда не было верным...

Дальнейшее расследование означает фиксацию ошибки "до 50% регрессии производительности в контрольных тестах Controls" (для доступа к ней необходимо войти в систему).

Похоже, что у них была проблема с производительностью, и во время исследования заметил, что в коде была ошибка (это было похоже на мой пример выше). Они исправили ошибку и добавили комментарий, чтобы уточнить, что это исправление не повлияло на производительность благодаря предсказанию ветвлений.

Выдержки:

[..] глядя на код, я заметил ошибку в случае, когда ни один из детей скина не управляется. В таком случае minX/Y и maxX/Y в конечном итоге будут MAX/MIN_VALUE, где мы действительно хотим, чтобы они были равны нулю. Таким образом, этот набор изменений исправляет эту проблему. В ходе тестирования я увидел небольшое (~1 кадр / с) улучшение производительности, поэтому я не думаю, что это изменение решает проблему производительности. В любом случае, код должен быть таким, какой он есть.

[...] Обратите внимание, что при использовании MIN/MAX была ошибка - эти значения не являются самыми большими и самыми маленькими значениями для Float и Double (это имело бы смысл по симметрии с целочисленными типами, но это не так как они были указаны). Для выполнения операций min/max над значениями с плавающей запятой вы хотите использовать вместо этого значения NEGATIVE/POSITIVE_INFINITY, чтобы получить результаты, которые вы ищете.

Предложение "предсказание перехода благоприятствует наиболее часто используемому условию" ничего не говорит о значении оцениваемого условия, будь то положительное или отрицательное. Это только говорит о том, что предсказание ветвления может помочь условным ветвям, которые используются чаще, то есть тем, которые находятся в цикле. Так это в основном говорит, используя if внутри петли в порядке.

Хотя вывод верен, вам не следует беспокоиться о ifв цикле (вам не о чем беспокоиться, если профилировщик не скажет вам, что существует узкое место), само предложение довольно бессмысленно в контексте Java.

Предсказание ветвей - это функция ЦП, поэтому при интерпретируемом выполнении оно не имеет отношения к ветвям уровня Java, поскольку они просто изменяют состояние интерпретатора (т. Е. Указатель, который будет считан следующей инструкцией), но не связаны с инструкцией ЦП, которая была бы специфичной для конкретная ветка.

Как только включается HotSpot, картина совершенно иная. Если этот код является горячей точкой, оптимизатор применяет множество преобразований кода, которые дают неверные предположения о том, как будет выполняться код Java.

Одна общая оптимизация - это развертывание цикла. Вместо того, чтобы иметь один блок кода, представляющий тело цикла, будет несколько его экземпляров, следующих друг за другом, оптимизированных с учетом инвариантов их конкретной итерации. Эти настройки позволяют исключить if связанные ветви, так как вполне предсказуемо, что после первого перехода firstManagedChild от true в false, он никогда не вернется, следовательно, в то время как первая итерация неизменно видит true значение для него, код для последующих итераций может быть оптимизирован для обработки переменной как постоянно false,

Таким образом, в этом случае предсказание ветвлений снова не будет иметь смысла, так как для if заявление, исход которого можно предсказать заранее.

Здесь можно найти довольно подробную статью о прогнозировании ветвлений.

Ответить на ваш вопрос - насколько я понимаю, нет. Я не думаю, что отрицание "если" будет иметь какое-либо значение. Это оптимизирует условие повторения ложных значений точно так же, как и для нескольких истинных значений.

В дополнение к существующим ответам:

Этот комментарий, кажется, не является оправданием для "отрицания, если" (так как это не должно иметь никакого значения с точки зрения производительности в любом случае). Вместо этого это, вероятно, оправдание для того, чтобы не пытаться "оптимизировать" код и избежать единого 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 будет практически свободным из-за предсказания ветвления.

Примечание: я думаю, что могут быть и другие "микрооптимизации", например, избегание Math#min(double,double) , Но ребята из JavaFX (надеюсь) знают свое дело и, вероятно, сделали бы это, если бы это имело значение в их случае)

Другие вопросы по тегам