Я всегда задавался вопросом, имеет ли вообще какое-либо значение (производительность) объявление общей переменной перед циклом, в отличие от повторяющихся внутри цикла? Пример (довольно бессмысленный) в Java:
а) объявление перед циклом:
double intermediateResult;
for(int i=0; i < 1000; i++){
intermediateResult = i;
System.out.println(intermediateResult);
}
б) объявление (повторно) внутри цикла:
for(int i=0; i < 1000; i++){
double intermediateResult = i;
System.out.println(intermediateResult);
}
Какой из них лучше, а или б ?
Я подозреваю, что повторное объявление переменных (пример b ) теоретически создает дополнительные издержки , но компиляторы достаточно умны, чтобы это не имело значения. Преимущество примера b состоит в том, что он более компактен и ограничивает область действия переменной тем местом, где она используется. Тем не менее, я склонен кодировать в соответствии с примером а .
Изменить: я особенно заинтересован в случае Java.
java
performance
loops
variables
initialization
Rabarberski
источник
источник
Ответы:
Что лучше, а или б ?
С точки зрения производительности, вам придется его измерять. (И, на мой взгляд, если вы можете измерить разницу, компилятор не очень хорош).
С точки зрения обслуживания, b лучше. Объявите и инициализируйте переменные в одном и том же месте, в самой узкой области видимости. Не оставляйте зазор между объявлением и инициализацией и не загрязняйте пространства имен, которые вам не нужны.
источник
Ну, я запускал ваши примеры A и B по 20 раз каждый, повторяя 100 миллионов раз (JVM - 1.5.0).
A: среднее время выполнения: 0,074 сек.
B: среднее время выполнения: 0,067 сек.
К моему удивлению, Б был немного быстрее. С такой скоростью, как компьютеры, сейчас трудно сказать, сможете ли вы точно измерить это. Я бы тоже написал код A, но я бы сказал, что это не имеет значения.
источник
Это зависит от языка и точного использования. Например, в C # 1 это не имеет значения. В C # 2, если локальная переменная захвачена анонимным методом (или лямбда-выражением в C # 3), это может иметь очень существенное значение.
Пример:
Вывод:
Разница в том, что все действия захватывают одну и ту же
outer
переменную, но у каждого своя отдельнаяinner
переменная.источник
Outer
быть 9?Вот что я написал и скомпилировал в .NET.
Это то, что я получаю от .NET Reflector, когда CIL возвращается в код.
Так что после компиляции оба выглядят одинаково. В управляемых языках код преобразуется в CL / байт-код, а во время исполнения он преобразуется в машинный язык. Таким образом, на машинном языке двойка может даже не быть создана в стеке. Это может быть просто регистр, поскольку код отражает, что это временная переменная для
WriteLine
функции. Существует целый набор правил оптимизации только для циклов. Так что средний парень не должен беспокоиться об этом, особенно в управляемых языках. Есть случаи, когда вы можете оптимизировать управление кодом, например, если вам нужно объединить большое количество строк, используя толькоstring a; a+=anotherstring[i]
vs, используяStringBuilder
, Существует очень большая разница в производительности между ними. Есть много таких случаев, когда компилятор не может оптимизировать ваш код, потому что он не может понять, что задумано в большей области. Но это может в значительной степени оптимизировать основные вещи для вас.источник
Это гоча в VB.NET. Результат Visual Basic не будет повторно инициализировать переменную в этом примере:
Это будет печатать 0 в первый раз (переменные Visual Basic имеют значения по умолчанию при объявлении!), Но
i
каждый раз после этого.Если вы добавите
= 0
, однако, вы получите то, что ожидаете:источник
Я сделал простой тест:
против
Я скомпилировал эти коды с помощью gcc - 5.2.0. А затем я разобрал main () этих двух кодов, и вот результат:
1º:
против
2º
Которые точно так же, как результат. разве это не доказательство того, что два кода производят одно и то же?
источник
Это зависит от языка - IIRC C # оптимизирует это, так что нет никакой разницы, но JavaScript (например) будет каждый раз выполнять весь процесс выделения памяти.
источник
Я бы всегда использовал A (а не полагался на компилятор) и мог бы также переписать:
Это все еще ограничивается
intermediateResult
областью действия цикла, но не повторяется во время каждой итерации.источник
На мой взгляд, b - лучшая структура. В последнем значении middleResult остается после завершения цикла.
Изменить: Это не имеет большого значения для типов значений, но ссылочные типы могут быть несколько весомыми. Лично мне нравится, что переменные разыменовываются как можно скорее для очистки, и b делает это для вас,
источник
sticks around after your loop is finished
- хотя это не имеет значения в языке, подобном Python, где связанные имена держатся до конца функции.my
ключевым словом), C # и Java для названия 5, которое я использовал.Я подозреваю, что некоторые компиляторы могут оптимизировать оба кода, чтобы они были одним и тем же кодом, но, конечно, не всем. Так что я бы сказал, что вам лучше с первым. Единственная причина последнего - если вы хотите убедиться, что объявленная переменная используется только в вашем цикле.
источник
Как правило, я объявляю свои переменные в самой внутренней области видимости. Итак, если вы не используете промежуточный результат вне цикла, то я бы пошел с B.
источник
Сотрудник предпочитает первую форму, говоря, что это оптимизация, предпочитая повторно использовать декларацию.
Я предпочитаю второй (и пытаюсь убедить моего сотрудника! ;-)), прочитав это:
В любом случае, это относится к категории преждевременной оптимизации, которая зависит от качества компилятора и / или JVM.
источник
Существует разница в C #, если вы используете переменную в лямбде и т. Д. Но в целом компилятор будет делать то же самое, предполагая, что переменная используется только внутри цикла.
Учитывая, что они в основном одинаковы: обратите внимание, что версия b делает читателей гораздо более очевидным, что переменная не используется и не может использоваться после цикла. Кроме того, версия b гораздо легче реорганизована. В версии a сложнее извлечь тело цикла в его собственный метод.Более того, версия b уверяет вас, что такой рефакторинг не имеет побочных эффектов.
Следовательно, версия a бесит меня бесконечно, потому что в этом нет никакой пользы, и это значительно усложняет анализ кода ...
источник
Ну, вы всегда можете сделать это для:
Таким образом, вы объявляете переменную только один раз, и она умрет, когда вы выйдете из цикла.
источник
Я всегда думал, что если вы объявляете свои переменные внутри цикла, то вы тратите впустую память. Если у вас есть что-то вроде этого:
Тогда не только объект должен быть создан для каждой итерации, но также должна быть новая ссылка, выделенная для каждого объекта. Кажется, что если сборщик мусора работает медленно, у вас будет куча свисающих ссылок, которые необходимо очистить.
Однако, если у вас есть это:
Затем вы создаете только одну ссылку и каждый раз назначаете ей новый объект. Конечно, это может занять немного больше времени, чтобы выйти из области видимости, но тогда есть только одна свисающая ссылка, чтобы иметь дело с.
источник
Я думаю, что это зависит от компилятора и трудно дать общий ответ.
источник
Моя практика следующая:
если тип переменной прост (int, double, ...), я предпочитаю вариант b (внутри).
Причина: уменьшение области видимости переменной.
если тип переменной не простой (какой-то тип
class
илиstruct
), я предпочитаю вариант а (снаружи).Причина: уменьшение количества вызовов ctor-dtor.
источник
С точки зрения производительности, снаружи (намного) лучше.
Я выполнил обе функции по 1 млрд раз каждая. outside () заняло 65 миллисекунд. inside () заняло 1,5 секунды.
источник
Я тестировал на JS с Node 4.0.0, если кому-то интересно. Объявление вне цикла привело к повышению производительности примерно на 0,5 мс в среднем за 1000 испытаний со 100 миллионами итераций цикла на испытание. Так что я собираюсь сказать «вперед» и написать это наиболее читаемым / поддерживаемым способом, который B, IMO. Я бы поместил свой код в скрипку, но я использовал модуль Node для повышения производительности. Вот код:
источник
А) безопаснее, чем Б) ......... Представьте себе, если вы инициализируете структуру в цикле, а не в int или float, что тогда?
лайк
Вы наверняка столкнетесь с проблемами утечки памяти! Поэтому я полагаю, что «А» - более безопасная ставка, в то время как «В» уязвим для накопления памяти, особенно при работе с библиотеками с закрытым исходным кодом.
источник
Это интересный вопрос. Исходя из моего опыта, при обсуждении этого вопроса для кода возникает главный вопрос:
Есть ли причина, по которой переменная должна быть глобальной?
Имеет смысл объявить переменную только один раз, глобально, а не много раз локально, потому что это лучше для организации кода и требует меньше строк кода. Однако, если его нужно объявить только локально в одном методе, я бы инициализировал его в этом методе, чтобы было ясно, что переменная относится исключительно к этому методу. Будьте осторожны, чтобы не вызывать эту переменную вне метода, в котором она инициализируется, если вы выберете последний вариант - ваш код не будет знать, о чем вы говорите, и сообщит об ошибке.
Также, как примечание, не дублируйте имена локальных переменных между различными методами, даже если их цели почти идентичны; это просто сбивает с толку.
источник
это лучшая форма
1) таким способом объявляется один раз раз и переменная, и не каждая за цикл. 2) Назначение это более толстый, чем все другие варианты. 3) Таким образом, правило bestpractice - это любое объявление вне итерации для.
источник
Попробовал то же самое в Go и сравнил вывод компилятора с
go tool compile -S
помощью go 1.9.4Нулевая разница согласно выходу ассемблера.
источник
У меня был тот же самый вопрос в течение длительного времени. Итак, я протестировал еще более простой кусок кода.
Вывод: Для таких случаев есть NO разница в производительности.
Случай внешней петли
Чехол внутри петли
Я проверил скомпилированный файл на декомпиляторе IntelliJ и в обоих случаях получил одно и то же
Test.class
Я также разобрал код для обоих случаев, используя метод, приведенный в этом ответе . Я покажу только те части, которые имеют отношение к ответу
Случай внешней петли
Чехол внутри петли
Если вы обратите внимание, только
Slot
назначеноi
иintermediateResult
вLocalVariableTable
обменена в качестве продукта порядка их появления. Такая же разница в слоте отражена в других строках кода.intermediateResult
в обоих случаях это локальная переменная, поэтому нет разницы во времени доступа.БОНУС
Компиляторы делают массу оптимизации, взгляните на то, что происходит в этом случае.
Нулевой случай
Нулевая работа декомпилирована
источник
Даже если я знаю, что мой компилятор достаточно умен, я не буду полагаться на него и буду использовать вариант а).
Вариант б) имеет смысл для меня, только если вам отчаянно нужно сделать промежуточный результат недоступным после тела цикла. Но я все равно не могу себе представить такую отчаянную ситуацию ...
РЕДАКТИРОВАТЬ: Джон Скит сделал очень хорошую мысль, показав, что объявление переменной внутри цикла может иметь реальную семантическую разницу.
источник