#java
#java
Вопрос:
Посмотрите на этот Java-код:
class PerformanceTest2{
public static void main(String args[]){
Long sum = 0L;
for(int i=0;i<Integer.MAX_VALUE;i )
sum = i;
System.out.println("Sum = " sum);
}
}
Замечено, что для этого кода требуется больше времени, поскольку сумма «Длинная», а не «длинная». Итак, на каждой итерации происходит следующее:
sum = new Long(sum.longValue() i); (for sum =i;)
Итак, каждый раз создается новый объект. Разве Java не поддерживает C -подобную функцию возврата ссылки, чтобы мы могли написать (возможно):
sum.longValue() = i;
возможно, не нужно создавать объект sum каждый раз в цикле? Я прав?
Ответ №1:
Ну, он не вызывает конструктор. Он использует:
for (int i = 0; i < Integer.MAX_VALUE; i )
{
long tmp = sum.longValue(); // Unboxing
tmp = i;
sum = Long.valueOf(tmp); // Boxing
}
Объекты-оболочки намеренно неизменяемы — их можно было бы легко спроектировать изменяемыми, но неизменяемость часто является очень полезной функцией. Если вы хотите написать свой собственный изменяемый тип оболочки, добро пожаловать — в этот момент у вас может быть такой код, как:
LongWrapper sum = new LongWrapper(0L);
for (int i = 0; i < Integer.MAX_VALUE; i )
{
sum.add(i);
}
System.out.println("Sum = " sum);
Или, возможно:
LongWrapper sum = new LongWrapper(0L);
for (int i = 0;i < Integer.MAX_VALUE; i )
{
sum.setValue(sum.getValue() i);
}
System.out.println("Sum = " sum);
Комментарии:
1. программа с «длинной» суммой выполнялась за 1.660 с, в то время как эта («Длинная»сумма) заняла 9.367с. Так может ли это быть причиной? И разве Java не создает sum каждый раз в цикле?
2. @AmitL: Да, бокс почти наверняка является причиной.
Long.valueOf
Метод будет вызываться на каждой итерации, но это не обязательно создает новое значение. Например, значения от -128 до 127 включительно гарантированно будут кэшироваться.3. Пример кода очень быстро выйдет за пределы диапазона
{-128, 127}
. Я думаю, можно с уверенностью сказать, что почти на каждой итерации будет вызываться конструктор (посредствомLong.valueOf()
). По крайней мере, на начальном проходе через цикл. И, вероятно, также при последующих проходах, поскольку маловероятно, что Java будет кэшировать где-либо рядомInteger.MAX_VALUE
с экземплярамиLong
.4. На самом деле я немного удивлен, что JIT не упрощает код соответствующим образом (должно быть выполнимо). Вероятно, потому, что это именно «неправильный путь». Обычно вы используете примитивы и позволяете компилятору автоматически привязывать его к объекту, если это необходимо.
5. @Voo: Не забывайте, что любая оптимизация, о которой должен знать JIT-компилятор, требует времени, пока он выполняет компиляцию JIT. Имеет больше смысла тратить это время на оптимизацию разумного кода, чем на то, чтобы понять, как глупый код можно заставить выглядеть не глупо.
Ответ №2:
Я приглашаю вас взглянуть на тестовые примеры, которые я создал здесь:
Ваш код медленный не потому, что вы смешиваете long
и int
вводите, а потому, что вы используете Long
вместо long
. Long
Тип является правильным объектом и неизменяемым при загрузке, поэтому каждый раз, когда вы присваиваете своей переменной новое значение, создается новый объект (возможное исключение — если кэшированный объект уже существует для нового значения). Это дорогостоящая операция (условно говоря).
Как вы увидите из примера кода, изменение цикла на добавление a long
вместо an int
не ускоряет его выполнение. Способ ускорить это — изменить первую переменную на a long
вместо a Long
.
Ответ №3:
В Java нет ссылок, подобных C . Кроме того, встроенные классы-оболочки для примитивных типов намеренно сделаны неизменяемыми. Одна из причин этого решения заключается в том, что во время выполнения может затем кэшировать экземпляры оболочки для определенных значений и избегать необходимости создавать новый объект (для этого требуется, чтобы вы вызывали valueOf
вместо выделения нового объекта через new
; компилятор делает это для бокса).
Ответ №4:
Итак, каждый раз создается новый объект. Разве Java не поддерживает C -подобную функцию возврата ссылки, чтобы мы могли написать (возможно): …
Если вы используете Long
, вы явно запрашиваете тип оболочки из Java. И соглашение для типов оболочек таково: они неизменяемы. И неизменность (как постоянство в C ) требует, чтобы никакие изменяемые внутренние элементы не передавались извне. Но ссылка, подобная C , точно сделает это. (Давайте пропустим часть ссылки на const, потому что это также не поможет вам в C .)
возможно, не нужно создавать объект sum каждый раз в цикле? Я прав?
Теоретически да, но если вы хотите такого поведения, почему бы вам не использовать not a plain long
с самого начала?
Комментарии:
1. итак, в данной программе создается сумма каждый раз в цикле? Надеюсь, что нет.
2. Конечно. Вместо этого вы просили
Long
«youl get it. It's that simple. If you don't want that, use
long».
Ответ №5:
Другие уже объяснили, почему Long
занимает больше времени long and how using
, чем Long.valueOf` может быть немного быстрее.
Пожалуйста, не позволяйте этому быть причиной отказа от использования Long. По всей вероятности, это не повлияет на общее время пропускной способности вашей системы.
Если есть жесткие циклы, где это влияет на производительность, тогда используйте long
там примитив, свернутую вручную оболочку, как описывает Джон, или MutableLong из apache commons.
Комментарии:
1. На самом деле я не вижу никаких причин использовать «long» в этой ситуации. Благодаря автоматической блокировке мы все равно получаем объект, если это необходимо, и не имеем накладных расходов на производительность.
2. Если я правильно вас понимаю, вы говорите, что можете просто использовать, например, a
Set<Long>
иput
along
в нем, и автобоксинг позаботится о преобразовании. Правильно, просто знайте, что автофиксация также будет преобразовываться с использованиемvalueOf
(проверьте в javap) и будет иметь те же накладные расходы.3. Ну, очевидно. Но компилятор будет автоматически блокировать примитив только тогда, когда это необходимо. Следовательно, мы получаем преимущества примитивов, когда это возможно, без каких-либо недостатков. Если мы сделаем это наоборот, мы столкнемся именно с теми проблемами производительности, которые мы видим здесь. До автоматической блокировки использование Long имело смысл — это помогло избежать дополнительных вызовов функций, но сегодня? Не вижу никаких преимуществ.