2015-06-01 3 views
5

В настоящее время я работаю над диссертацией бакалавра о том, как написать эффективный Java-код. Следующие четыре фрагмента кода являются частью теста JMH, который будет выполнять каждый метод по 1 миллиону раз каждый.Как Java определяет, какой оператор в математическом выражении должен быть (un) в штучной упаковке?

public final static int primitiveOnly(int dummy, int add1, int add2) { 
    for(int i = 0; i < 10; i++) { 
     dummy += (add1 + add2); 
    } 
    return dummy; 
} 
public final static int primitiveToWrapper(int dummy, int add1, Integer add2) { 
    for(int i = 0; i < 10; i++) { 
     dummy += (add1 + add2); 
    } 
    return dummy; 
} 
public final static int wrapperToPrimitive(Integer dummy, Integer add1, int add2) { 
    for(int i = 0; i < 10; i++) { 
     dummy += (add1 + add2); 
    } 
    return dummy; 
} 
public final static Integer wrapperToWrapper(Integer dummy, Integer add1, Integer add2) { 
    for(int i = 0; i < 10; i++) { 
     dummy += (add1 + add2); 
    } 
    return dummy; 
} 

Результаты для этого четырех методов:

  • primitiveOnly: 1,7 мс/операции
  • primitiveToWrapper: 2.2 мс/операция
  • wrapperToPrimitive: 47,5 мс/операции
  • wrapperToWrapper: 48,2 мс/операция

Причиной такого поведения было бы то, что во время операции в primitiveToWrapper значение Integer просто должно быть unboxed, где в операции в wrapperToPrimitive должен быть установлен первый операнд в Integer, что приводит к дорогостоящему созданию объекта.

Есть ли определенная причина, по которой Java ведет себя так? Я прочитал The Java Language Specification, но не смог найти ответ на этот вопрос.

UPDATE:

Для решения точки относительно возвращаемых значений (спасибо Phil Anderson) я обновил свой код. Кроме того, я изменил все переменные Integer в классе тестов на int. Это новая версия:

public final static int primitiveOnly(int dummy, int add1, int add2) { 
    for(int i = 0; i < 10; i++) { 
     dummy += (add1 + add2); 
    } 
    return dummy; 
} 

public final static int primitiveToWrapperIntDummy(int dummy, int add1, Integer add2) { 
    for(int i = 0; i < 10; i++) { 
     dummy += (add1 + add2); 
    } 
    return dummy; 
} 

public final static Integer primitiveToWrapperIntegerDummy(Integer dummy, int add1, Integer add2) { 
    for(int i = 0; i < 10; i++) { 
     dummy += (add1 + add2); 
    } 
    return dummy; 
} 

public final static int wrapperToPrimitiveIntDummy(int dummy, Integer add1, int add2) { 
    for(int i = 0; i < 10; i++) { 
     dummy += (add1 + add2); 
    } 
    return dummy; 
} 

public final static Integer wrapperToPrimitiveIntegerDummy(Integer dummy, Integer add1, int add2) { 
    for(int i = 0; i < 10; i++) { 
     dummy += (add1 + add2); 
    } 
    return dummy; 
} 

public final static int wrapperToWrapperIntDummy(int dummy, Integer add1, Integer add2) { 
    for(int i = 0; i < 10; i++) { 
     dummy += (add1 + add2); 
    } 
    return dummy; 
} 

public final static Integer wrapperToWrapperIntegerDummy(Integer dummy, Integer add1, Integer add2) { 
    for(int i = 0; i < 10; i++) { 
     dummy += (add1 + add2); 
    } 
    return dummy; 
} 

Результаты в среднем 10 итераций (1 итерация = 1 миллион выполнений каждого метода выше).

  • primitiveOnly: 0,783
  • primitiveToWrapper (интермедиат фиктивный): 0,735
  • primitiveToWrapper (целое число фиктивный): 24,999
  • WrapperToPrimitive (интермедиат фиктивный): 0,709
  • WrapperToPrimitive (целое число фиктивный): 26,782
  • WrapperToWrapper (int dummy): 0.764
  • WrapperToWrapper (Integer dummy): 27.301

Окончательные результаты теперь более интуитивно понятны. Спасибо всем за то, что помогли мне.

+0

Весь эталон содержит в общей сложности 4 метода для каждой возможной комбинации int и Integer. Я отредактирую его. – whatTheFox

+1

Обратите внимание, что даже с JMH результаты должны быть взяты с солью. Из-за размера фиксированной петли и «фиктивности» операций вы почти никогда не знаете, что на самом деле делает JIT. – Marco13

ответ

3

Во втором бите кода каждый раз, когда вы назначаете значение фиктивной java, нужно вставить его в целое, потому что это тип переменной. Он не знает, что вы на самом деле никогда не называете какие-либо методы, и что это может быть простой int.

Поэтому каждый раз, когда он набирает код dummy += (add1 + add2);, он должен сделать следующее.

  • Unbox фиктивная
  • Выполните дополнения
  • Box результат обратно в фиктивной

Он будет делать это каждый раз через цикл.

+0

Правда, но так как типы данных с самого начала понятны, почему Java не просто переключает операнды и выполняет простую операцию распаковки вместо дорогого бокса? Должно быть возможным, поскольку добавление является коммутативным и каждая переменная в выражении вычисляется до начала операции. – whatTheFox

+1

Я не уверен, что понимаю вашу точку зрения. В конечном счете, он присваивает int манекену. Поскольку манекен имеет тип Integer, он должен его вставить. Нельзя этого избежать. –

+0

Точка взята. Я просто переписал часть кода, чтобы рассмотреть операцию бокса манекена. Добавление Integer к int еще медленнее, чем наоборот ... Я добавлю новый код и результаты к моему вопросу. – whatTheFox

2

Это связано с тем, что когда dummy является Целочисленным, его значение неизменно. См. Why are Integers immutable in Java?

В основном, в последнем способе, когда вы пишете dummy += (add1 + add2);, это означает

dummy = Integer.valueOf(dummy.intValue() + add1.intValue() + add2.intValue()); 

Каждый раз в цикле, новый объект должен быть выделен, чтобы сохранить новое целое значение.

Смежные вопросы