Может ли компилятор Java оптимизировать циклы, чтобы они возвращались раньше? - PullRequest
0 голосов
/ 14 декабря 2018

Я работаю с внешней библиотекой, которая решила обрабатывать коллекции самостоятельно.Я не работаю с ним или не могу обновить его.Для работы с элементами этой сторонней «коллекции» он возвращает только итераторы.

В ходе проверки кода возник вопрос о наличии в коде нескольких возвратов для повышения производительности.Мы все согласны (в команде), что код более читабелен с помощью одного возврата, но некоторые беспокоятся об оптимизации.

Я знаю, что преждевременная оптимизация плоха.Это тема для другого дня.

Я полагаю, что JIT-компилятор может с этим справиться и пропустить ненужные итерации, но не смог найти никакой информации, подтверждающей это. Способен ли JIT на такое?

Пример кода рассматриваемой проблемы:

public void boolean contains(MyThings things, String valueToFind) {
    Iterator<Thing> thingIterator = things.iterator();
    boolean valueFound = false;
    while(thingIterator.hasNext()) {
        Thing thing = thingIterator.next();
        if (valueToFind.equals(thing.getValue())) {
            valueFound = true;
        }
    }
    return valueFound;
}

VS

public void boolean contains(MyThings things, String valueToFind) {
    Iterator<Thing> thingIterator = things.iterator();
    while(thingIterator.hasNext()) {
        Thing thing = thingIterator.next();
        if (valueToFind.equals(thing.getValue())) {
            return true;
        }
    }
    return false;
}

Ответы [ 2 ]

0 голосов
/ 14 декабря 2018

Мы все согласны с тем, что код более читабелен при одном возврате.

Не совсем.Это просто структурированное программирование старой школы, когда функции, как правило, не были небольшими, а парадигмы сохранения неизменных значений еще не были популярны.строк кода), которые возвращаются в разных точках.Например, в рекурсивных методах у вас обычно есть по крайней мере один базовый случай, который возвращается немедленно, и другой, который возвращает значение, возвращаемое рекурсивным вызовом.

Часто вы обнаружите, что создание дополнительной переменной результата, просто для хранения возвращаемого значения, и затем проверка того, что никакая другая часть функции не перезаписывает результат, когда вы уже знаете, что можете просто вернуть, просто создает шумчто делает его менее читаемым, а не более.Читатель должен разобраться с когнитивной перегрузкой, чтобы увидеть, что результат не изменяется дальше вниз.Во время отладки это еще больше увеличивает боль.

Я не думаю, что ваш пример - преждевременная оптимизация.Это логическая и критическая часть вашего алгоритма поиска.Вот почему вы можете break из циклов или, в вашем случае, просто вернуть значение.Я не думаю, что JIT может понять, что он легко может разорвать петлю.Он не знает, хотите ли вы изменить переменную обратно на false, если вы найдете что-то еще в коллекции.(Я не думаю, что разумно осознавать, что valueFound не меняется обратно на false).

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

0 голосов
/ 14 декабря 2018

Java-компилятор не может выполнить такую ​​оптимизацию, потому что выполнение этого в общем случае изменило бы логику программы.

В частности, добавление раннего возврата изменило бы количество вызовов thingIterator.hasNext(),потому что ваш первый блок кода продолжает повторять коллекцию до конца.

Java может потенциально заменить break на ранний возврат, но это повлияет на время выполнения программы.

...