2016-04-11 4 views
11

У меня есть цикл for, который работает 4096 раз, и он должен быть как можно быстрее. Производительность здесь действительно важна. В настоящее время я использую методы getter внутри цикла, которые просто возвращают значения или объекты из полей, которые не изменяются во время цикла.Java использует getter in for loop или создает локальную переменную?

Пример:

for (;;) { 
    doSomething(example.getValue()); 
} 

Есть ли накладные расходы с помощью методов получения? Быстро ли это происходит следующим образом?

Пример:

Object object = example.getValue(); 
for (;;) { 
    doSomething(object); 
} 

Если да, то, что верно и для доступа к общественным поля как example.value?

Редактировать: Я не использую System.out.println() внутри цикла.

Редактировать: Некоторые поля не являются final. Нет полей volatile, а метод (геттер) не равен synchronized.

+0

Сохранение вывода в локальной строке будет быстрее, чем геттер или разыменование объекта. Но это измеряется в «teensy» – Jamie

ответ

7

Как Rogério answered, получив ссылку на объект вне цикла (Object object = example.getValue();), вероятно, будет быстрее (или не будет по крайней мере, никогда не будет работать медленнее), чем вызов сорбент внутри цикла, поскольку

  • в «худшем» случае , example.getValue() могли бы действительно сделать некоторые очень дорогостоящие вещи в фоновом режиме, несмотря на то, что getter methods должны быть «тривиальными». Назначая ссылку один раз и повторно используя ее, вы делаете это дорогостоящее вычисление только один раз.
  • в «лучшем» случае, example.getValue() делает что-то тривиальное, например return value;, и поэтому его назначение внутри цикла будет не дороже, чем вне цикла после компилятора JIT inlines the code.

Однако более важным является различием в семантике между ними и его возможными последствиями в многопоточной среде: Если состояние объекта example изменений в пути, который вызывает example.getValue() возвращать ссылки на различные объекты, возможно, что на каждой итерации метод doSomething(Object object) фактически будет работать в другом экземпляре Object, позвонив по телефону doSomething(example.getValue());. С другой стороны, с помощью вызова поглотителя вне цикла и установив ссылку на возвращаемом экземпляр (Object object = example.getValue();), doSomething(object); будет работать на objectп раз для п итераций.

Это различие в семантике может привести к тому, что поведение в многопоточной среде будет радикально отличаться от поведения в однопоточной среде. Более того, это не обязательно актуальная проблема многопоточности в памяти: если example.getValue() зависит от, например, базы данных/HDD/сетевых ресурсов, возможно, что эти данные изменяются во время выполнения цикла, что позволяет вернуть другой объект, даже если приложение Java является однопоточным.По этой причине лучше всего рассмотреть, что вы на самом деле хотите выполнить с помощью своего цикла, а затем выбрать вариант, который наилучшим образом отражает предполагаемое поведение.

0

Если вам необходимо запустить его как можно быстрее, вы не должны использовать System.out.println в критических разделах.

Что касается геттера: для использования геттера есть небольшие накладные расходы, но вы не должны беспокоиться об этом. У Java есть оптимизация getter и setter в JIT-компиляторе. Поэтому в конечном итоге они будут заменены собственным кодом.

+1

Я не использую 'System.out.println()' в своем коде. Это просто плохой пример. – stonar96

4

Это зависит от получателя.

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

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

Если геттер равен synchronized, то использование локальной переменной будет значительно быстрее, поскольку блокировки не нужно получать и освобождать каждый вызов, но код цикла будет использовать потенциально устаревшее значение поля во время Геттер был вызван.

+0

Спасибо за ваш ответ. Весь код однопоточный. Таким образом, нет изменчивых полей или синхронных методов. Геттеры - простые геттеры. – stonar96

4

Вы должны предпочитать локальную переменные вне цикла, по следующим причинам:

  1. Он стремится сделать код более удобным для чтения/понимание, избегая вложенные вызовы методов, как doSomething(example.getValue()) в одной строке коды , и позволяя коду давать более точное, более конкретное имя для значения, возвращаемого методом getter.
  2. Не все методы геттера тривиальны (т. Е. Иногда они выполняют некоторую потенциально дорогостоящую работу), но разработчики часто этого не замечают, предполагая, что данный метод тривиален и недорог, когда это действительно не так. В таких случаях код может сильно пострадать, если разработчик не сможет его реализовать. Извлечение в локальную переменную имеет тенденцию избегать этой проблемы.
1

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

  1. 4096 не так много, поэтому, если это не закончится за очень короткое время, не беспокойтесь о производительности.
  2. Если в этом цикле есть что-то еще более дорогостоящее, то геттер не имеет значения.
  3. Преждевременная оптимизация - это корень всего зла. Сосредоточьтесь на том, чтобы сделать код правильным и четким. Затем измерьте и профилируйте его и сузите самую дорогую вещь, и позаботьтесь об этом. При необходимости улучшите фактический алгоритм.

Что касается вашего вопроса, то я не знаю точно, что делает JIT, но если он не может доказать с уверенностью, что example.getValue() или example.value не изменяется в цикле (что трудно сделать, если поле не final и геттер тривиален), то логически невозможно, чтобы он не мог неоднократно называть геттер в прежнем образце, поскольку это могло бы изменить поведение программы. Повторные звонки, безусловно, являются ненужным количеством дополнительной работы.

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

+0

"*, тогда логически не существует способа избежать неоднократного вызова геттера *" => метод getter все еще может быть встроен, независимо от того, что он делает ... – assylias

+0

@assylias да, но это еще какая-то работа, даже если он просто проверяет значение поля. –

Смежные вопросы