В java более эффективно использовать byte или short вместо int и float вместо double?

я заметил, что я всегда использовал int и удваивается независимо от того, насколько маленьким или большим должно быть число. Итак, в java более эффективно использовать byte или short вместо int и float вместо double?

предположим у меня есть программа с множеством целых чисел и удваивается. Стоит ли проходить и менять мои Инты на байты или шорты, если я знаю, что номер подойдет?

Я знаю, что 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 и short case, он выполняет дополнительно инструкции преобразуйте промежуточное 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 может увеличить производительность, если вы используете их в огромном количестве. Вот такой эксперимент:--5-->

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)

теперь я меняю ints на байты в TestClass и снова запускаю. Вот результат:

 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 не упоминают набор инструкций для этих типов данных. Инструкции как магазин, нагрузка, добавляют etc. есть версии для типа данных 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 и локальной переменной матрицы. Он также мотивирован частотой данных int в типичном программы. Другие интегральные типы имеют менее прямую поддержку. Нет байтовые, char или короткие версии инструкций по хранению, загрузке или добавлению, например.

цитата из спецификации JVM присутствует здесь (стр. 58).