шаблон Java: когда имеет смысл использовать временные переменные - PullRequest
13 голосов
/ 26 февраля 2012

Так что я часто делаю что-то вроде следующего шаблона.Вместо:

if (map.containsKey(someKey)) {
    Value someValue = map.get(someKey);
    ...
}

Чтобы не пересекать карту дважды (и так как я знаю, что моя карта не хранит нули), я сделаю:

Value someValue = map.get(someKey);
if (someValue != null) {
    ...
}

Это мне кажетсячтобы быть достойным паттерном, учитывая, что операции Map выполняют приличное количество операций, а оптимизатор, я полагаю, недостаточно умен, чтобы его оптимизировать.

Но потом я обнаруживаю, что делаю подобные паттерны в других ситуациях.Например, я должен сохранить результат someMethod() во временной переменной вместо того, чтобы сделать вызов дважды?Ясно, что я не могу вызвать someMethod() дважды, если есть побочные эффекты, но когда имеет смысл вызывать его только один раз с точки зрения оптимизации?

if (someObject.someMethod() != null) {
    processSomehow(someObject.someMethod());
}

Я знаю, что это граничит с "неконструктивным""вопрос, поэтому я ищу ответы, которые представляют некоторые факты и ссылки, а не просто догадки.

  • Когда имеет смысл это делать, а когда нет?
  • Какя должен оценить «стоимость» someMethod(), чтобы определить, когда следует использовать временную переменную?
  • Для простых методов, таких как методы get, это может помешать компилятору горячей точки или оптимизатору ина самом деле производите менее эффективный код?

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

Спасибо за любую информацию.

Ответы [ 6 ]

11 голосов
/ 26 февраля 2012

Как мне оценить «стоимость» someMethod (), чтобы определить, когда следует использовать временную переменную?

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

Лично меня не волнуют проблемы производительности, вызванные объявлением временной переменной. Я просто сохраняю возвращаемое значение вместо вызова метода дважды. Код намного легче читать и понимать (если только вы не назовете все эти переменные просто temp ;-))

5 голосов
/ 26 февраля 2012

Как правило, я использую локальную переменную, когда она мне нужна более одного раза.Вы можете дать этому локальному var красивое имя, и все знают, о чем этот var.С точки зрения производительности: локальные переменные дешевы, а методы могут быть сложными.

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

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

5 голосов
/ 26 февраля 2012

Ясно, что я не могу вызвать someMethod () дважды, если есть побочные эффекты, но когда имеет смысл вызывать его только один раз с точки зрения оптимизации?

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

2 голосов
/ 26 февраля 2012

Некоторые люди цитируют Кнута (неявно или явно), но я думаю, что это всегда имеет смысл как шаблон проектирования (и случаи, когда null является допустимым ключом, , как указано AH , становитсяэто гораздо яснее).

На мой взгляд, это похоже на случай передачи переменной по константной ссылке в C ++ вместо значения.Для этого есть две причины:

  1. . Он сообщает, что значение не изменится
  2. , но основная причина этого заключается в том, что он незначительно быстрее.

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

2 голосов
/ 26 февраля 2012

Для меня это все сводится к логике программы, а не к производительности. Итак

if (map.containsKey(someKey)) {
    Value someValue = map.get(someKey);
    ...
}

не эквивалентно этому коду во всех случаях:

Value someValue = map.get(someKey);
if (someValue != null) {
    ...
}

Рассмотрим карту, где значение может быть нулевым для данного ключа. Если часть ... безопасна null, то код ведет себя иначе.

Также этот код с каким-то образом имеет логические проблемы. Учтите это:

BufferedRead reader = ...;
if (reader.readLine() != null) {
    processSomehow(reader.readLine());
}

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

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

1 голос
/ 26 февраля 2012

Напоминает мне об обсуждении аналогичного стиля использования .NET Dictionary: Что является более эффективным: словарь TryGetValue или ContainsKey + Item?

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

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

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