Почему примитивный тип short значительно медленнее, чем long или int?

Я попытался оптимизировать использование оперативной памяти в игре для Android, изменив примитивы int на shorts. Прежде чем я сделал это, меня интересовала производительность примитивных типов в Java.

Поэтому я создал этот небольшой тестовый бенчмарк, используя библиотеку суппорта.

public class BenchmarkTypes extends Benchmark {

    @Param("10") private long testLong;
    @Param("10") private int testInt;
    @Param("10") private short testShort;


    @Param("5000") private long resultLong = 5000;
    @Param("5000") private int resultInt = 5000;
    @Param("5000") private short resultShort = 5000;

    @Override
    protected void setUp() throws Exception {
        Random rand = new Random();

        testShort = (short) rand.nextInt(1000);
        testInt = (int) testShort;
        testLong = (long) testShort;
    }

    public long timeLong(int reps){
        for(int i = 0; i < reps; i++){
            resultLong += testLong;
            resultLong -= testLong;         
        }
        return resultLong;
    }

    public int timeInt(int reps){
        for(int i = 0; i < reps; i++){
            resultInt += testInt;
            resultInt -= testInt;           
        }
        return resultInt;
    }

    public short timeShort(int reps){
        for(int i = 0; i < reps; i++){
            resultShort += testShort;
            resultShort -= testShort;
        }
        return resultShort;
    }
}

Результаты теста меня удивили.

Проверить обстоятельства

Бенчмарк запускаем под библиотекой Caliper.

Результаты тестирования

https://microbenchmarks.appspot.com/runs/0c9bd212-feeb-4f8f-896c-e027b85dfe3b< /а>

Целое 2,365 нс

Длинный 2,436 нс

Короткий 8,156 нс

Заключение теста?

Короткий примитивный тип значительно медленнее (в 3-4 раза), чем длинный и примитивный тип int?

Вопрос

  1. Почему примитив short значительно медленнее, чем int или long? Я ожидаю, что примитивный тип int будет самым быстрым на 32-битной виртуальной машине, а длинный и короткий будут равны по времени или короткий будет еще быстрее.

  2. Это также относится к телефонам Android? Зная, что телефоны Android обычно работают в 32-битной среде, и сейчас все больше и больше телефонов начинают поставляться с 64-битными процессорами.


person Rolf ツ    schedule 19.06.2014    source источник
comment
Вы не разогрели JIT. Вы не сделали достаточно итераций. Это не то, как вы микробенчируете Java.   -  person Boris the Spider    schedule 19.06.2014
comment
Это (скорее всего) вызвано преобразованием Java Short (каждый раз) в int (или long) для арифметических операций.   -  person Germann Arlington    schedule 19.06.2014
comment
@GermannArlington - Нет. Настоящее объяснение 1000-кратной разницы во времени заключается в том, что тест написан неправильно. См. связанные вопросы и ответы.   -  person Stephen C    schedule 19.06.2014
comment
Правда, никакого реального эталона здесь нет! Но в 1000 раз медленнее? Будет ли создание хорошего эталона иметь такое большое значение?   -  person Rolf ツ    schedule 19.06.2014
comment
Рольф - прочитайте первый комментарий к ответу, на который вы ссылаетесь. Человек, написавший этот ответ, вероятно, совершил ту же ошибку, что и вы.   -  person Stephen C    schedule 19.06.2014
comment
Рольф – Конечно, может сработать. Попробуйте переписать свой бенчмарк, следуя рекомендациям, и посмотрите, что получится.   -  person Stephen C    schedule 19.06.2014
comment
Поскольку ваш бенчмарк ошибочен, ваш результат недействителен, и нет особого смысла пытаться его объяснить... как если бы это был реальный результат. (И это не просто немного неверно. Замедление в 1000 раз просто невероятно.) Хотя этот Вопрос не является строго дубликатом другого Вопроса, ответы на другой Вопрос являются лучшим ответом на этот.   -  person Stephen C    schedule 19.06.2014
comment
Если вы обновили вопрос, указав действительный эталонный тест и действительные результаты, возможно его стоит открыть повторно...   -  person Stephen C    schedule 19.06.2014
comment
Вот доказательство (иш) - stackoverflow.com/a/14532605/139985. Проблема не в окружающей среде. Дело в том, что ваш тест не похож на то, что делает реальное приложение... и на то, что типичная JVM предназначена для эффективной работы. Скорее всего, медленная часть вашего теста искажена JIT-компиляцией. Фактически вы измеряете время, затрачиваемое на JIT-компиляцию и оптимизацию кода!   -  person Stephen C    schedule 19.06.2014
comment
Обновлен вопрос с новыми результатами теста с использованием библиотеки java-калипера.   -  person Rolf ツ    schedule 19.06.2014
comment
Обратите внимание, что то, что вы делаете, не является операцией, и достаточно умный JIT может сдуть все это. Это легко исправить (я бы предложил использовать += отдельно или в сочетании с ^=). @StephenC Текущая причина закрытия больше не является правильной, поэтому я бы предложил повторно открыть (хотя она может быть закрыта как дубликат вопроса, на который вы ссылаетесь).   -  person maaartinus    schedule 19.06.2014
comment
@RolfSmit Лучше показать, что вы пытались убедиться, что ваш тест имеет смысл, чем просить доказательства того, что это не так. Объяснение поведения иногда может занять несколько часов, и мы не хотим, чтобы время было потрачено впустую. То, что делают бенчмаркеры, должно быть близко к тому, что происходит в реальности, при условии, что приложение работает достаточно долго (именно для этого и предназначалась Java). Ведь на Android может быть иначе.   -  person maaartinus    schedule 19.06.2014
comment
Я согласен, я пытаюсь оптимизировать использование ОЗУ, использование шорт улучшит его. Но я не хочу, чтобы производительность падала. Что я должен делать? Объекты, использующие шорты, используются много!   -  person Rolf ツ    schedule 19.06.2014
comment
Повторно открыт - теперь у этого вопроса есть разумный ориентир с более правдоподобным набором цифр. Однако я совершенно уверен, что на этот вопрос полностью отвечает один, на который ссылается @StephenC. Маловероятно, что использование short уменьшит объем используемой памяти.   -  person Boris the Spider    schedule 19.06.2014


Ответы (2)


Байт-код Java не поддерживает базовые операции (+, -, *, /, >>, >>>, ‹‹, %) над примитивными типами меньше, чем int. В системе команд просто нет выделенных для таких операций байт-кодов. Таким образом, виртуальная машина должна преобразовать тип short(s) в тип int(s), выполнить операцию, затем обрезать int обратно в тип short и сохранить его в результате.

Проверьте сгенерированный байт-код с помощью javap, чтобы увидеть разницу между вашими короткими тестами и тестами int.

Оптимизация VM/JIT, по-видимому, сильно смещена в сторону операций int/long, что имеет смысл, поскольку они наиболее распространены.

Типы меньше, чем int, имеют свое применение, но в первую очередь для экономии памяти в массивах. Они не так хорошо подходят, как простые члены класса (конечно, вы все равно используете их, когда это подходящий тип для данных). Элементы меньшего размера могут даже не уменьшать размер объектов. Текущие виртуальные машины (опять же) в основном ориентированы на скорость выполнения, поэтому виртуальная машина может даже выравнивать поля по границам собственных машинных слов, чтобы повысить производительность доступа за счет расхода памяти.

person Durandal    schedule 19.06.2014

Это возможно из-за того, как java/android обрабатывает целочисленную арифметику в отношении примитивов, которые меньше, чем int.

Когда в java добавляются два примитива с типом данных меньше, чем int, они автоматически повышаются до целочисленного типа данных. Приведение обычно требуется для преобразования результата обратно в необходимый тип данных.

Хитрость заключается в сокращенных операциях, таких как +=, -= и т. д., когда приведение происходит неявно, так что окончательный результат операции:

resultShort += testShort;

на самом деле похоже на что-то вроде этого:

resultShort = (short)((int) resultShort + (int) testShort);

Если мы посмотрим на дизассемблированный байт-код метода:

public static int test(int a, int b){
    a += b;
    return a;
}

мы видим:

public static int test(int, int);
    Code:
       0: iload_0       
       1: iload_1       
       2: iadd          
       3: istore_0      
       4: iload_0       
       5: ireturn   

сравнивая это с идентичным методом с заменой типа данных для краткости, мы получаем:

public static short test(short, short);
    Code:
       0: iload_0       
       1: iload_1       
       2: iadd          
       3: i2s           
       4: istore_0      
       5: iload_0
       6: ireturn

Обратите внимание на дополнительную инструкцию i2s (от целого до короткого). Это вероятный виновник потери производительности. Еще одна вещь, которую вы можете заметить, это то, что все инструкции основаны на целых числах, обозначаемых префиксом i (например, iadd означает сложение целых чисел). Это означает, что где-то на этапе iload шорты были повышены до целых чисел, что также может привести к снижению производительности.

Если вы можете поверить мне на слово, байт-код для длинных арифметических операций идентичен целочисленному, за исключением того, что инструкции относятся к длинным (например, ladd вместо iadd).

person initramfs    schedule 19.06.2014
comment
Этот ответ находится на правильном пути. Однако важно помнить, что JVM не выполняет байт-коды напрямую (после JIT-компиляции). Следовательно, байт-коды напрямую не объясняют разницу. Например, если собственный набор инструкций имел прямую поддержку арифметики 16, а JIT-компилятор был достаточно умен, чтобы использовать ее, то можно было бы ожидать, что short арифметика будет быстрее, чем long арифметика. - person Stephen C; 20.06.2014
comment
Но реальность такова, что наборы инструкций для ПК, серверов и даже смартфонов настроены на 32-битные/64-битные операции, а не на 16-битные операции. Следовательно, для выполнения 16-битной арифметики обычно требуется больше родных инструкций и больше тактовых циклов, что делает ее медленнее, чем 32- и 64-битную... как показывает бенчмаркинг OP. Но это сильно зависит от аппаратного обеспечения целевой платформы... и, возможно, от JIT-компилятора. - person Stephen C; 20.06.2014