Как убедить JVM встроить метод интерфейса? - PullRequest
2 голосов
/ 27 сентября 2011

У меня есть иерархия классов, основанная на интерфейсе и реализованная с помощью абстрактного базового класса.Это выглядит примерно так:

interface Shape {
  boolean checkFlag();
}

abstract class AbstractShape implements Shape {
  private boolean flag = false;

  protected AbstractShape() { /* compute flag value */ }

  public final boolean checkFlag() { return flag; }
}

interface HasSides extends Shape { 
  int numberOfSides();
}

interface HasFiniteArea extends Shape { 
  double area();
}

class Square extends AbstractShape implements HasSides, HasFiniteArea {

} 

class Circle extends AbstractShape implements HasFiniteArea { 
}

/** etc **/

Когда я пробую исполняемый код с помощью VisualVM, выясняется, что AbstractShape.checkFlag () никогда не встроен и потребляет 14% от общего времени выполнения программы , что непристойно для такого простого метода, даже для так часто вызываемого метода.

Я пометил метод final в базовом классе, и (в настоящее время) все классы, реализующие интерфейс "Shape", расширяют AbstractShape.

Правильно ли я интерпретирую результаты примера VisualVM?Есть ли способ убедить JVM встроить этот метод или мне нужно вырвать интерфейсы и просто использовать абстрактный базовый класс?(Я бы предпочел этого не делать, потому что иерархия включает интерфейсы, такие как HasFiniteArea и HasSides, что означает, что иерархия не имеет идеальной древовидной формы)

РЕДАКТИРОВАТЬ: для ясности, это метод, который в любой вселенная должна быть встроенной.Он вызывается более 420 миллионов раз за 2 минуты выполнения, и, поскольку он не встроен и остается виртуальным вызовом, он составляет 14% времени выполнения.Я задаю вопрос: что мешает JVM встроить этот метод и как я могу это исправить?

Ответы [ 4 ]

5 голосов
/ 27 сентября 2011

Вот цитата из Википедии

Распространенным заблуждением является то, что объявление класса или метода финальным повышает эффективность, позволяя компилятору напрямую вставлять метод inline, где бы он ни вызывался. Это не совсем верно; компилятор не может сделать это, потому что классы загружаются в время выполнения и не может быть той же версией, что и те, которые были просто скомпилирован. Кроме того, среда выполнения и JIT-компилятор имеют информация о том, какие именно классы были загружены, и могут ли принимать лучшие решения о том, когда встраивать, или нет Метод окончательный.

См. Также эту статью .

1 голос
/ 27 сентября 2011

Пороговое значение компилятора по умолчанию - 10000. -XX:CompilerThreshold= Это означает, что метод или цикл (для серверной JVM) необходимо вызывать как минимум 10000 раз, прежде чем он будет скомпилирован в собственный код.

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

профилировщики пробуют пример кода и назначают время. Это не всегда делает большую работу, и вы получаете методы, которые очевидны, а не время потребителям назначается процессорное время. VisualVM - это бесплатный профилировщик, который реализуется на Java. Если вы используете профилировщик, такой как YourKit, вы можете получить более точные результаты, поскольку он использует собственный код, например не создает мусор.

0 голосов
/ 29 сентября 2011

После долгих экспериментов я не смог заставить Sun JDK 6 встроить этот метод при вызове интерфейса.

К счастью, было задействовано ограниченное количество сайтов вызовов, и изменение

public void paint(Shape shape) {
  if(shape.checkFlag()) { /* do stuff */ }
} 

до

public void paint(Shape shape) {
  if(((AbstractShape)shape).checkFlag()) { /* .. */ }
}

достаточно, чтобы заставить JVM встроить метод. Время выполнения рассматриваемого вычисления уменьшилось на 13% по сравнению с первоначальным временем выполнения 6 минут.

0 голосов
/ 27 сентября 2011

Из литературы, которую я читал о старых версиях JVM, объявляя методы final, можно определить, что этот метод будет встроен. Теперь вам не нужно указывать этот метод для окончательной оптимизации кода.

Вы должны позволить JVM оптимизировать код и сделать метод окончательным, только если вы явно не хотите, чтобы этот метод был переопределен. JVM, вероятно, не делает ваш метод встроенным, потому что его собственная оптимизация быстрее, учитывая остальную часть кода приложения.

...