Иногда в ходе алгоритма нам нужно вычислить или просто сохранить несколько значений, которые зависят друг от друга или не имеют никакого отношения друг к другу.Может ли JVM опустить создание недолговечных объектов, поэтому я могу делать рефакторинг без ущерба для производительности?
Подобно тому, как (довольно бессмысленный, но то, что вопросы, простой) пример позволяет найти два различных значения в int[]
, которые находятся ближе всего к числу 3
:
int a = values[0];
int b = values[1];
for (int value : values) {
int distance = Math.abs(value-3);
if (value != b) {
if (distance < Math.abs(a-3)) {
a = value;
}
}
if (value != a) {
if (distance < Math.abs(b-3)) {
b = value;
}
}
}
takeSausageSliceBetween(a, b);
Мы не можем просто сделать методы computeA()
и computeB()
в том же классе, потому что вычисления для a
и b
являются взаимозависимыми. Так вычисления a
и b
просто просят, чтобы быть переработан в отдельный класс:
class DistinctNumbersNearestTo3 {
final int a;
final int b;
DistinctNumbersNearestTo3(int[] values) {
// same algorithm
}
}
поэтому выше код уровня становится красиво и ясно:
DistinctNumbersNearestTo3 nearest = new DistinctNumbersNearestTo3(values);
takeSausageSliceBetween(nearest.a, nearest.b);
// nearest never leaves the method it was declared in
Однако (пожалуйста, поправьте меня, если я ошибаюсь) он вводит, по крайней мере для некоторых уровней оптимизации, экземпляр, а затем сбор мусора (только от eden, но в любом случае) от нового объекта только для того, чтобы быть понятным с вашим кодом. А также поиск int в стеке превращается в поиск объекта в куче.
Вопрос: Является ли JVM достаточно умным, чтобы в конечном итоге оптимизировать реорганизованный код для работы так же, как и нефакторизованный код?
Другой случай, когда я хотел бы кучу вместе несколько переменных в новый объект только для чистого кода, когда я хочу, чтобы реорганизовать длинный метод a()
в несколько новых методов путем введения нового класса C
который содержит данные, относящиеся к a()
и поэтому содержит несколько новых методов для работы с этими данными. Я наткнулся на такой случай при реализации класса, представляющего набор цепей, к которым мы можем добавлять новые ссылки (пары узлов), поэтому расширяя существующую цепочку, объединяя две цепи вместе в одну или создавая новую цепочку из двух узлов. Давайте обсудим этот конкретный класс только для некоторых перспективы:
public class SetOfChains {
List<List<Node>> chains;
public void addLink(Node a, Node b) {
// Very long method that mutates field this.chains. Needs refactoring.
}
}
Чтобы правильно разделить addLink
на несколько методов SetOfChains
, каждый из этих новых методов будет иметь много параметров, потому что есть данные, относящиеся к addLink()
вызова, что необходимо на каждом этапе addLink()
. Сохранение этих данных в выделенных областях SetOfChains
было бы возможно, но вонючим и невыразительным (потому что снова это имеет смысл только во время вызова метода), поэтому, очевидно, я создаю новый внутренний класс для хранения этих данных в своих полях и выполняю весь алгоритм шаги, мутирует внешнее поле chains
как unrefactored addLink()
делает:
class SetOfChains{
List<List<Node>> chains;
void addLink(Node a, Node b) {
new LinkAddition(a,b).compute();
}
class LinkAddiditon { // Instances never leave addLink()'s scope
private final Something bunch, of, common, fields;
compute() {
this.properly();
this.isolated();
this.steps();
this.of();
this.the();
this.refactored();
this.method();
}
// There be code for the properly isolated steps...
}
Но так fuctionally, что эквивалентно тому, что мой старый длинный unrefactored addLink()
, может JVM сделать полученные инструкции быть оптимальным, так как в unrefactored случае, как если бы не было объектов LinkAddition
?
Таким образом, чтобы нарисовать нижнюю черту под этой стеной текста: Если я стремлюсь к легко понять код путем извлечения данных и методов к новым классов, а не методы, это обязательно ввести снижение производительности?
И правильно ли я понимаю, что описанные случаи - это именно то, о чем escape analysis?
Думаю, вы хотели сослаться на Кнута с цитатой в первом предложении. Однако фактическая цитата говорит о том, что преждевременная оптимизация является корнем всего зла и, как правило, не осуждает оптимизацию. Я согласен с другими частями вашего ответа. – box
Да, вы правы, я знаю, что это был Кнут, и я должен был поставить «преждевременный» в цитате. Я обновлю спасибо – dkatzel