Я заметил, что всегда использую int и double, независимо от того, насколько маленьким или большим должно быть число. Итак, в java более эффективно использовать byte
или short
вместо int
и float
вместо double
?
Итак, предположим, у меня есть программа с множеством целых и двойных чисел. Стоит ли перебирать и изменять мои целые числа на байты или короткие, если бы я знал, что число подойдет?
Я знаю, что в Java нет беззнаковых типов, но могу ли я что-нибудь сделать, если бы знал, что число будет только положительным?
Под эффективным я в основном имею в виду обработку. Я бы предположил, что сборщик мусора был бы намного быстрее, если бы все переменные были половинными, и эти вычисления, вероятно, тоже были бы немного быстрее. (Думаю, поскольку я работаю над Android, мне тоже нужно немного побеспокоиться о баране)
(Я бы предположил, что сборщик мусора работает только с объектами, а не с примитивами, но все же удаляет все примитивы в заброшенных объектах, верно?)
Я попробовал это с небольшим приложением для Android, которое у меня есть, но вообще не заметил разницы. (Хотя я ничего «научно» не измерял.)
Я ошибаюсь, полагая, что это должно быть быстрее и эффективнее? Я бы не хотел пройти через все и изменить все в масштабной программе, чтобы узнать, что я зря потратил свое время.
Стоит ли делать это с самого начала, когда я начинаю новый проект? (Я имею в виду, я думаю, что каждая мелочь могла бы помочь, но опять же, если так, то почему кажется, что никто не делает это.)
источник
Это зависит от реализации JVM, а также от базового оборудования. Большинство современного оборудования не будет извлекать отдельные байты из памяти (или даже из кеша первого уровня), т.е. использование более мелких примитивов обычно не снижает потребление полосы пропускания памяти. Точно так же современные процессоры имеют размер слова 64 бита. Они могут выполнять операции с меньшим количеством бит, но это работает за счет отбрасывания лишних битов, что тоже не быстрее.
Единственное преимущество состоит в том, что более мелкие примитивные типы могут привести к более компактной структуре памяти, особенно при использовании массивов. Это экономит память, что может улучшить локальность ссылок (таким образом, уменьшить количество промахов кеша) и снизить накладные расходы на сборку мусора.
Однако, вообще говоря, использование более мелких примитивных типов не быстрее.
Чтобы продемонстрировать это, взгляните на следующий тест:
package tools.bench; import java.math.BigDecimal; public abstract class Benchmark { final String name; public Benchmark(String name) { this.name = name; } abstract int run(int iterations) throws Throwable; private BigDecimal time() { try { int nextI = 1; int i; long duration; do { i = nextI; long start = System.nanoTime(); run(i); duration = System.nanoTime() - start; nextI = (i << 1) | 1; } while (duration < 100000000 && nextI > 0); return new BigDecimal((duration) * 1000 / i).movePointLeft(3); } catch (Throwable e) { throw new RuntimeException(e); } } @Override public String toString() { return name + "\t" + time() + " ns"; } public static void main(String[] args) throws Exception { Benchmark[] benchmarks = { new Benchmark("int multiplication") { @Override int run(int iterations) throws Throwable { int x = 1; for (int i = 0; i < iterations; i++) { x *= 3; } return x; } }, new Benchmark("short multiplication") { @Override int run(int iterations) throws Throwable { short x = 0; for (int i = 0; i < iterations; i++) { x *= 3; } return x; } }, new Benchmark("byte multiplication") { @Override int run(int iterations) throws Throwable { byte x = 0; for (int i = 0; i < iterations; i++) { x *= 3; } return x; } }, new Benchmark("int[] traversal") { @Override int run(int iterations) throws Throwable { int[] x = new int[iterations]; for (int i = 0; i < iterations; i++) { x[i] = i; } return x[x[0]]; } }, new Benchmark("short[] traversal") { @Override int run(int iterations) throws Throwable { short[] x = new short[iterations]; for (int i = 0; i < iterations; i++) { x[i] = (short) i; } return x[x[0]]; } }, new Benchmark("byte[] traversal") { @Override int run(int iterations) throws Throwable { byte[] x = new byte[iterations]; for (int i = 0; i < iterations; i++) { x[i] = (byte) i; } return x[x[0]]; } }, }; for (Benchmark bm : benchmarks) { System.out.println(bm); } } }
который печатается на моем несколько старом блокноте (добавление пробелов для настройки столбцов):
int multiplication 1.530 ns short multiplication 2.105 ns byte multiplication 2.483 ns int[] traversal 5.347 ns short[] traversal 4.760 ns byte[] traversal 2.064 ns
Как видите, разница в производительности незначительна. Оптимизация алгоритмов намного важнее выбора примитивного типа.
источник
short
иbyte
они более эффективны при хранении в массивах, которые достаточно велики, чтобы иметь значение (чем больше массив, тем больше разница в эффективности;byte[2]
может быть больше или менее эффективен, чем anint[2]
, но не настолько, чтобы иметь значение в любом случае), но отдельные значения более эффективно сохраняются какint
.Использование
byte
вместоint
может повысить производительность, если вы используете их в большом количестве. Вот эксперимент:import java.lang.management.*; public class SpeedTest { /** Get CPU time in nanoseconds. */ public static long getCpuTime() { ThreadMXBean bean = ManagementFactory.getThreadMXBean(); return bean.isCurrentThreadCpuTimeSupported() ? bean .getCurrentThreadCpuTime() : 0L; } public static void main(String[] args) { long durationTotal = 0; int numberOfTests=0; for (int j = 1; j < 51; j++) { long beforeTask = getCpuTime(); // MEASURES THIS AREA------------------------------------------ long x = 20000000;// 20 millions for (long i = 0; i < x; i++) { TestClass s = new TestClass(); } // MEASURES THIS AREA------------------------------------------ long duration = getCpuTime() - beforeTask; System.out.println("TEST " + j + ": duration = " + duration + "ns = " + (int) duration / 1000000); durationTotal += duration; numberOfTests++; } double average = durationTotal/numberOfTests; System.out.println("-----------------------------------"); System.out.println("Average Duration = " + average + " ns = " + (int)average / 1000000 +" ms (Approximately)"); }
}
Этот класс проверяет скорость создания нового
TestClass
. Каждый тест выполняет это 20 миллионов раз, а всего 50 тестов.Вот TestClass:
public class TestClass { int a1= 5; int a2= 5; int a3= 5; int a4= 5; int a5= 5; int a6= 5; int a7= 5; int a8= 5; int a9= 5; int a10= 5; int a11= 5; int a12=5; int a13= 5; int a14= 5; }
Я запустил
SpeedTest
класс и в итоге получил следующее:Average Duration = 8.9625E8 ns = 896 ms (Approximately)
Теперь я меняю целые числа на байты в TestClass и снова запускаю его. Вот результат:
Average Duration = 6.94375E8 ns = 694 ms (Approximately)
Я считаю, что этот эксперимент показывает, что если вы создаете экземпляры огромного количества переменных, использование byte вместо int может повысить эффективность.
источник
byte
могло быть >> медленнее <<, чемint
.байт обычно считается 8-битным. short обычно считается 16-битным.
В «чистой» среде, которая не является java, поскольку вся реализация байтов, длинных, коротких и других забавных вещей обычно скрыта от вас, byte лучше использует пространство.
Однако ваш компьютер, вероятно, не 8-битный, и, вероятно, не 16-битный. это означает, что, в частности, для получения 16 или 8 битов потребуется прибегнуть к «хитрости», которая тратит время, чтобы притвориться, что у него есть возможность обращаться к этим типам, когда это необходимо.
На данный момент это зависит от того, как реализовано оборудование. Однако, как я понял, лучшая скорость достигается за счет хранения вещей в кусках, которые удобны для вашего процессора. 64-битный процессор любит иметь дело с 64-битными элементами, а для чего-то меньшего часто требуется «инженерная магия», чтобы притвориться, что ему нравится с ними работать.
источник
Одна из причин меньшей производительности short / byte / char заключается в отсутствии прямой поддержки этих типов данных. Под прямой поддержкой это означает, что в спецификациях JVM не упоминается какой-либо набор инструкций для этих типов данных. Такие инструкции, как store, load, add и т. Д., Имеют версии для типа данных int. Но у них нет версий для short / byte / char. Например, рассмотрим ниже Java-код:
void spin() { int i; for (i = 0; i < 100; i++) { ; // Loop body is empty } }
То же самое преобразуется в машинный код, как показано ниже.
0 iconst_0 // Push int constant 0 1 istore_1 // Store into local variable 1 (i=0) 2 goto 8 // First time through don't increment 5 iinc 1 1 // Increment local variable 1 by 1 (i++) 8 iload_1 // Push local variable 1 (i) 9 bipush 100 // Push int constant 100 11 if_icmplt 5 // Compare and loop if less than (i < 100) 14 return // Return void when done
Теперь рассмотрите возможность изменения int на short, как показано ниже.
void sspin() { short i; for (i = 0; i < 100; i++) { ; // Loop body is empty } }
Соответствующий машинный код изменится следующим образом:
0 iconst_0 1 istore_1 2 goto 10 5 iload_1 // The short is treated as though an int 6 iconst_1 7 iadd 8 i2s // Truncate int to short 9 istore_1 10 iload_1 11 bipush 100 13 if_icmplt 5 16 return
Как вы можете заметить, для управления коротким типом данных он все еще использует версию инструкции типа данных int и при необходимости явно преобразует int в short. Теперь из-за этого снижается производительность.
Вот причина отказа от прямой поддержки:
Цитируется из представленной здесь спецификации JVM (стр. 58).
источник
javac
компилятором, и вы не можете сделать из них какие-либо надежные выводы о том, как программа будет работать в реальной жизни. Компилятор JIT компилирует эти байт-коды в реальные машинные инструкции и в процессе выполняет довольно серьезную оптимизацию. Если вы хотите проанализировать производительность кода, вам необходимо изучить инструкции собственного кода. (И это сложно, потому что вам нужно учитывать временное поведение многоступенчатого конвейера x86_64.)Разница практически не заметна! Это скорее вопрос дизайна, соответствия, единообразия, привычки и т. Д. Иногда это просто вопрос вкуса. Когда все, что вас волнует, это то, что ваша программа запускается и работает, и замена a
float
на aint
не повредит правильности, я не вижу преимуществ в выборе того или другого, если вы не можете продемонстрировать, что использование любого типа изменяет производительность. Настройка производительности на основе типов, которые различаются 2 или 3 байтами, - это последнее, о чем вам следует заботиться; Дональд Кнут однажды сказал: «Преждевременная оптимизация - корень всех зол» (не уверен, что это был он, отредактируйте, если у вас есть ответ).источник
float
не может представлять все целые числа вint
банке; и не можетint
представлять любое нецелое значение, котороеfloat
может. То есть, хотя все значения int являются подмножеством длинных значений, int не является подмножеством float, а float не является подмножеством int.substituting a float for a double
, если это так, ответчик должен отредактировать ответ. Если он не отвечает, он должен от стыда опустить голову и вернуться к основам по причинам, указанным в @pst, и по многим другим причинам.