Влияние на производительность использования instanceof в Java - PullRequest
287 голосов
/ 19 сентября 2008

Я работаю над приложением, и один из подходов к проектированию предполагает чрезвычайно интенсивное использование оператора instanceof. Хотя я знаю, что дизайн ОО обычно старается избегать использования instanceof, это другая история, и этот вопрос связан исключительно с производительностью. Мне было интересно, есть ли какое-либо влияние на производительность? Это так же быстро, как ==?

Например, у меня есть базовый класс с 10 подклассами. В единственной функции, которая принимает базовый класс, я проверяю, является ли класс экземпляром подкласса, и выполняю некоторую процедуру.

Один из других способов решения этой проблемы - использовать вместо этого целочисленный примитив «id типа» и использовать битовую маску для представления категорий подклассов, а затем просто выполнить сравнение битовой маски для подкласса «id типа "постоянной маске, представляющей категорию.

instanceof каким-то образом оптимизируется JVM, чтобы быть быстрее, чем это? Я хочу придерживаться Java, но производительность приложения имеет решающее значение. Было бы здорово, если бы кто-то, кто был на этом пути раньше, мог бы дать совет. Не слишком ли я придираюсь к чему-то или сосредотачиваюсь не на том, что нужно оптимизировать?

Ответы [ 23 ]

4 голосов
/ 19 сентября 2008

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

4 голосов
/ 19 сентября 2008

'instanceof' на самом деле является оператором, как + или -, и я считаю, что у него есть собственная инструкция байт-кода JVM. Это должно быть достаточно быстро.

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

3 голосов
/ 19 сентября 2008

Трудно сказать, как конкретная JVM реализует экземпляр, но в большинстве случаев Объекты сравнимы со структурами, а также с классами, и каждая структура объекта имеет указатель на структуру класса, экземпляром которой он является. Так на самом деле instanceof для

if (o instanceof java.lang.String)

может быть так же быстро, как следующий код C

if (objectStruct->iAmInstanceOf == &java_lang_String_class)

при условии, что JIT-компилятор установлен и выполняет достойную работу.

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

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

3 голосов
/ 31 мая 2013

Я свяжусь с вами по поводу выступления. Но способ избежать проблемы (или ее отсутствия) вообще состоит в том, чтобы создать родительский интерфейс для всех подклассов, на которых вам нужно сделать instanceof. Интерфейс будет расширенным набором всех методов в подклассах, для которых вам нужно выполнить проверку экземпляра. Если метод не применяется к конкретному подклассу, просто предоставьте фиктивную реализацию этого метода. Если я не понял проблему неправильно, это то, как я обошел проблему в прошлом.

3 голосов
/ 19 сентября 2008

InstanceOf является предупреждением о плохом объектно-ориентированном дизайне.

Текущие JVM означают, что instanceOf сам по себе не является проблемой производительности. Если вы часто используете его, особенно для основной функциональности, возможно, пришло время взглянуть на дизайн. Прирост производительности (и простота / удобство обслуживания) от рефакторинга к лучшему дизайну значительно перевесит любые фактические циклы процессора, потраченные на фактический вызов instanceOf .

Чтобы привести очень маленький пример упрощенного программирования.

if (SomeObject instanceOf Integer) {
  [do something]
}
if (SomeObject instanceOf Double) {
  [do something different]
}

В случае плохой архитектуры лучшим выбором было бы, чтобы SomeObject был родительским классом двух дочерних классов, где каждый дочерний класс переопределяет метод (doSomething), поэтому код будет выглядеть так:

Someobject.doSomething();
2 голосов
/ 19 сентября 2008

В современной версии Java оператор instanceof работает быстрее, чем простой вызов метода. Это значит:

if(a instanceof AnyObject){
}

быстрее как:

if(a.getType() == XYZ){
}

Другое дело, если вам нужно каскадировать много экземпляров. Тогда переключатель, который вызывает только один раз getType (), быстрее.

2 голосов
/ 19 сентября 2008

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

if (o instanceof Class1)
   doThis();
else if (o instanceof Class2)
   doThat();
//...

Вы можете заменить это на

o.doEverything();

и затем имеют реализацию doEverything () в вызове Class1 «doThis ()», а в Class2 вызов «doThat ()» и т. Д.

1 голос
/ 27 июля 2011

Если скорость - ваша единственная цель, то использование int-констант для идентификации подклассов, кажется, сокращает миллисекунды времени

static final int ID_A = 0;
static final int ID_B = 1;
abstract class Base {
  final int id;
  Base(int i) { id = i; }
}
class A extends Base {
 A() { super(ID_A); }
}
class B extends Base {
 B() { super(ID_B); }
}
...
Base obj = ...
switch(obj.id) {
case  ID_A: .... break;
case  ID_B: .... break;
}

ужасный ОО дизайн, но если ваш анализ производительности показывает, что это то, где вы узкое место, то, возможно,. В моем коде код отправки занимает 10% от общего времени выполнения, и это может способствовать повышению общей скорости на 1%.

0 голосов
/ 16 октября 2017

Я подумал, что, возможно, стоит представить контрпример к общему мнению на этой странице, что "instanceof" не настолько дорог, чтобы о нем беспокоиться. Я обнаружил, что у меня есть некоторый код во внутреннем цикле, который (в какой-то исторической попытке оптимизации) сделал

if (!(seq instanceof SingleItem)) {
  seq = seq.head();
}

где вызов head () для SingleItem возвращает значение без изменений. Замена кода на

seq = seq.head();

дает мне ускорение с 269 мс до 169 мс, несмотря на то, что в цикле происходят довольно тяжелые вещи, такие как преобразование строки в двойное. Конечно, возможно, что ускорение происходит больше за счет устранения условного перехода, чем за счет исключения самого оператора; но я думал, что стоит упомянуть.

0 голосов
/ 13 августа 2013

Я также предпочитаю подход enum, но я бы использовал абстрактный базовый класс, чтобы заставить подклассы реализовать метод getType().

public abstract class Base
{
  protected enum TYPE
  {
    DERIVED_A, DERIVED_B
  }

  public abstract TYPE getType();

  class DerivedA extends Base
  {
    @Override
    public TYPE getType()
    {
      return TYPE.DERIVED_A;
    }
  }

  class DerivedB extends Base
  {
    @Override
    public TYPE getType()
    {
      return TYPE.DERIVED_B;
    }
  }
}
...