Преждевременная оптимизация в Java: когда использовать «x = foo.getX ()» против просто «foo.getX ()» - PullRequest
13 голосов
/ 31 августа 2010

Когда я вызываю один и тот же метод получения несколько раз, следует ли считать это проблемой?Лучше ли [всегда] присваивать локальную переменную и вызывать только один раз?

Я уверен, что ответ, конечно, «это зависит».

Меня больше беспокоит вопросболее простой случай, когда метод получения - это просто метод типа "передача значения частной переменной".т. е. не требуются дорогостоящие вычисления, не используются соединения с базой данных и т. д.

Мой вопрос о том, "лучше ли", касается как читаемости кода (стиля), так и производительности.то есть это большая часть попадания в производительность:

SomeMethod1(a, b, foo.getX(), c);
SomeMethod2(b, foo.getX(), c);
SomeMethod3(foo.getX());

против:

X x = foo.getX();
SomeMethod1(a, b, x, c);
SomeMethod2(b, x, c);
SomeMethod3(x);

Я понимаю, что этот вопрос немного придирчив и сер.Но я только что понял, у меня нет последовательного способа оценки этих компромиссов, вообще.Я ловлю рыбу по некоторым критериям, которые не просто причудливы.

Спасибо.

Ответы [ 8 ]

15 голосов
/ 31 августа 2010

На самом деле выбор должен зависеть не от снижения производительности, а от читабельности кода.

Когда вы создаете переменную, вы можете дать ей имя, которого она заслуживает в текущем контексте.Когда вы используете одно и то же значение более одного раза, оно, безусловно, имеет реальное значение, большее, чем имя метода (или, что еще хуже, цепочка методов).
И действительно лучше читать:

String username = user.getName();
SomeMethod1(a, b, username, c);
SomeMethod2(b, username, c);
SomeMethod3(username);

чем

SomeMethod1(a, b, user.getName(), c);
SomeMethod2(b, user.getName(), c);
SomeMethod3(user.getName());
8 голосов
/ 31 августа 2010

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

У меня, однако, есть принцип сохранения оператора в одной строке, что очень часто приводит к тому, что выражения типа "foo.getBar ()" слишком длинные, чтобы уместиться. Тогда мне будет удобнее извлечь его из локальной переменной («Bar bar = foo.getBar ()»).

6 голосов
/ 31 августа 2010

Это могут быть две разные вещи.

Если GetX недетерминирован, то 1-й даст результаты, отличные от 2-го

Лично я бы использовал 2-й.Это более очевидно и менее излишне многословно.

3 голосов
/ 31 августа 2010

Это зависит от того, что на самом деле делает getX(). Рассмотрим этот класс:

public class Foo {
    private X x;

    public X getX() { return x; }
}

В этом случае, когда вы делаете вызов на foo.getX(), JVM оптимизирует его полностью до foo.x (как в прямой ссылке на приватное поле foo, в основном указатель памяти ) . Однако, если класс выглядит так:

public class Foo {
    private X x;

    public X getX() { return cleanUpValue(x); }

    private X cleanUpValue(X x) {
        /* some modifications/sanitization to x such as null safety checks */
    }
}

JVM на самом деле больше не может использовать его так эффективно, поскольку согласно строительному контракту Foo он должен очистить x перед его выдачей.

Подводя итог, если getX() на самом деле ничего не делает, кроме возврата поля, то нет никакой разницы после запуска первоначальной оптимизации для байт-кода в том, вызываете ли вы метод только один или несколько раз.

3 голосов
/ 31 августа 2010

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

2 голосов
/ 01 сентября 2010

Обычно я храню его локально, если:

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

  2. Я собираюсь использовать его в длинной строке кода или функция и параметры очень длинные.

  3. Я хочу переименовать переменную влучше соответствовать поставленной задаче.

  4. Тестирование показывает значительное повышение производительности.

В противном случае мне нравится возможность получать текущие значения и нижеуровень абстракции вызовов методов.

2 голосов
/ 31 августа 2010

Большую часть времени я использовал бы getX, если бы он был только один раз, и создал бы для него переменную для всех остальных случаев. Часто просто для того, чтобы сохранить набор текста.

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

1 голос
/ 31 августа 2010

Необходимо учитывать две вещи:

  1. Есть ли у вызова getX () какие-либо побочные эффекты?Следуя установленным шаблонам кодирования, геттер не должен изменять объект, для которого он вызывается, в большинстве случаев побочный эффект отсутствует.Следовательно, семантически эквивалентно один раз вызвать метод получения и сохранить значение локально, а не несколько раз вызвать метод получения.(Эта концепция называется идемпотентностью - не имеет значения, вызываете ли вы метод один или несколько раз; эффект на данные точно такой же.)

  2. Если у геттера нет стороныВ результате компилятор может безопасно удалить последующие вызовы геттера и самостоятельно создать временное локальное хранилище - таким образом, код остается ультрачитаемым, и вы получаете все преимущества в скорости, вызывая геттер только один раз.Это тем более важно, если получатель не просто возвращает значение, но должен извлечь / вычислить значение или выполнить некоторые проверки.

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...