В java более эффективно использовать byte или short вместо int и float вместо double?
я заметил, что я всегда использовал int и удваивает независимо от того, насколько маленьким или большим должно быть число. Так что в java, это более эффективно использовать byte или short вместо int и float вместо double?
Итак, предположим, что у меня есть программа с большим количеством входов и дублей. Стоило бы пройти и изменить мои ints на байты или шорты, если бы я знал, что номер подойдет?
Я знаю, что java не имеет беззнаковых типов, но есть ли что-то дополнительное, что я мог бы если бы я знал, что число будет только положительным?
под эффективным я в основном имею в виду обработку. Я бы предположил, что сборщик мусора будет намного быстрее, если все переменные будут иметь половину размера, и что вычисления, вероятно, будут несколько быстрее.
(Я думаю, так как я работаю на android мне нужно немного беспокоиться о ram тоже)
(Я бы предположил, что сборщик мусора имеет дело только с объектами, а не примитивными, но все же удаляет все примитивы в заброшенных объектах, верно? )
я попробовал это с небольшим приложением для android, которое у меня есть, но на самом деле не заметил разницы. (Хотя я ничего" научно " не измерял.)
я ошибаюсь, предполагая, что это должно быть быстрее и эффективнее? Я не хотел бы пройти и изменить все в массивной программе, чтобы узнать, что я потратил свое время.
стоит ли это делать с самого начала, когда я начинаю новый проект? (Я имею в виду, я думаю, что каждый немного поможет, но опять же если да, то почему не похоже, что кто-то это делает.)
6 ответов:
я ошибаюсь, предполагая, что это должно быть быстрее и эффективнее? Я не хотел бы пройти и изменить все в массивной программе, чтобы узнать, что я потратил свое время.
короткий ответ:
Да, вы не ошибаетесь. В большинстве случаев, это делает небольшая разница С точки зрения используемого пространства.
это не стоит пытается оптимизировать это ... если у вас нет четких доказательств того, что оптимизация необходима. И если вы делаете нужно для оптимизации использования памяти объектных полей, в частности, вам, вероятно, потребуется принять другие (более эффективные) меры.
более длинный ответ
стеки моделей виртуальных машин Java и поля объектов с использованием смещений, которые (по сути) кратны 32-битному размеру примитивной ячейки. Поэтому, когда вы объявляете локальную переменную или поле объекта, как (скажем)
byte, переменная / поле будет храниться в 32-битной ячейке, как иint.есть два исключения из этого:
longиdoubleзначения требуют 2 примитивных 32-разрядных ячеек- массивы примитивных типов представлены в упакованном виде, так что (например) массив байтов содержит 4 байта на 32-битное слово.
так может стоит оптимизировать использование
longиdouble... и большие массивы примитивов. Но в целом нет.в теории, JIT может быть в состоянии оптимизировать это, но на практике я никогда не слышал о JIT, который делает. Одним из препятствий является то, что JIT обычно не может работать до тех пор, пока не будут созданы экземпляры компилируемого класса. Если JIT оптимизировал макет памяти, у вас может быть два (или более) "аромата" объекта одного и того же класса ... и это будет представлять огромные трудности.
Revisitation
глядя на результаты тестов в @ответ меритона, похоже, что с помощью
shortиbyteвместоintвлечет за собой штраф за умножение производительности. Действительно, если рассматривать операции изолированно, то штраф значителен. (Ты не должен ... но это совсем другое дело.)я думаю, что объяснение заключается в том, что JIT, вероятно, делает умножения с использованием 32-битных инструкций умножения в каждом случае. Но в
byteиshortcase, он выполняет дополнительно инструкции преобразуйте промежуточное 32-разрядное значение вbyteилиshortв каждой итерации цикла. (Теоретически это преобразование может быть сделано один раз в конце цикла ... но я сомневаюсь, что оптимизатор сможет это выяснить.)в любом случае, это указывает на другую проблему с переключением на
shortиbyteкак оптимизация. Это может сделать производительность хуже ... в алгоритме, то есть арифметики и вычислений.
это зависит от реализации 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Как вы можете видеть, различия в производительности довольно незначительны. Оптимизация алгоритмов гораздо важнее, чем выбор примитивного типа.
использование 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)"); }}
этот класс проверяет скорость создания нового тестового класса. Каждый тест делает это 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)теперь я изменяю ints в байты в тестовом классе и запустил его снова. Вот результат:
Average Duration = 6.94375E8 ns = 694 ms (Approximately)Я считаю, что этот эксперимент показывает, что если вы создаете огромное количество переменных, использование byte вместо int может повысить эффективность
байт обычно считается 8 бит. словом считается 16 бит.
в "чистой" среде, которая не является java, поскольку вся реализация байтов и длинных, коротких и других забавных вещей обычно скрыта от вас, byte лучше использует пространство.
однако, ваш компьютер, вероятно, не 8 бит, и 16 бит. это означает, что чтобы получить 16 или 8 бит, в частности, ему нужно будет прибегнуть к" обману", который тратит время чтобы притвориться, что у него есть возможность получить доступ к этим типам, когда это необходимо.
на данный момент, это зависит от того, как реализовано аппаратное обеспечение. Однако от меня было туго, лучшая скорость достигается от хранения вещей в кусках, которые удобны для вашего процессора, чтобы использовать. 64-битный процессор любит иметь дело с 64-битными элементами, и все, что меньше этого, часто требует "инженерной магии", чтобы притвориться, что ему нравится иметь с ними дело.
разница едва заметна! Это скорее вопрос дизайна, уместности, единообразия, привычки и т. д... Иногда это просто вопрос вкуса. Когда все, что вас волнует, это то, что ваша программа встает и работает и заменяет
floatнаintне повредит правильности, я не вижу никакого преимущества в переходе на тот или иной, если вы не можете продемонстрировать, что использование любого типа изменяет производительность. Настройка производительности на основе типов, которые отличаются в 2 или 3 байта действительно является последнее, о чем вы должны заботиться; Дональд Кнут однажды сказал: "преждевременная оптимизация-это корень всего зла" (не уверен, что это был он, отредактируйте, если у вас есть ответ).
одной из причин того, что short/byte/char менее эффективен, является отсутствие прямой поддержки этих типов данных. При прямой поддержке это означает, что спецификации JVM не упоминают какой-либо набор инструкций для этих типов данных. Инструкции, такие как магазин, загрузка, добавление и т. д. есть версии для типа данных 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, когда это необходимо. Теперь, из-за этого производительность уменьшается.
теперь причина, приведенная для не предоставления прямой поддержки следующим образом:
виртуальная машина Java обеспечивает самое прямое поддержка данных типа int. Отчасти это связано с ожиданием эффективной реализации стеков операндов виртуальной машины Java и локальной переменной матрицы. Оно также мотивирован частотой данных по инт в типичном программы. Другие интегральные типы имеют менее прямую поддержку. Нет байт, символ или короткие версии инструкции по хранению, загрузке или добавлению, например.
цитата из спецификации JVM присутствует здесь (стр. 58).
Comments