2014-02-06 2 views
8

Существует consensus, что использование интерфейсов лучше, чем использование классов. Я точно согласен: библиотечный метод, принимающий ArrayList вместо List, был бы дерьмом.Разница в производительности между передающим интерфейсом и классом перезагружена

Существует также консенсус в отношении того, что производительность всегда одна и та же. Здесь мой benchmark просит отличиться. results Существует от 1 до 4 реализаций как интерфейса, так и абстрактного класса. Когда используется более двух реализаций, производительность начинает расходиться. Я ищу объяснение этого поведения (а также происхождение ложного консенсуса).

+1

Я не согласен с обоими этими "консенсусов". Я тоже не удивлен результатами тестов. – Boann

+1

@Boann: Что случилось с первым? Не могли бы вы рассказать (может быть, в ответ)? – maaartinus

+0

Я ленился и думал, что кто-то быстрее ответит, но все равно я отдам его. – Boann

ответ

10

Существует мнение, что использование интерфейсов лучше, чем использование классов.

Это слишком упрощенно. Оба интерфейса и абстрактные классы имеют преимущества друг над другом.

Ответ, на который вы ссылаетесь, предлагает объявление переменных как java.util.List, а не java.util.ArrayList, где это возможно. Правильно, что использование List дает вам больше возможностей для выбора другого класса реализации позже, и, следовательно, это хорошо, когда вам не нужны методы, специфичные для ArrayList (например, .trimToCapacity()). Однако этот совет имеет ничего общего с интерфейсами или классами вообще, и это было бы так же верно, если java.util.List были абстрактным классом.

Существует также консенсус в отношении того, что производительность всегда одна и та же.

Популярным советом является то, что не следует беспокоиться о различиях в производительности между классами и интерфейсами и выбирать между ними на основе хороших принципов программирования. Это хороший совет, чтобы помешать программистам беспокоиться о несущественных различиях в производительности; однако иногда неправильно понимать, что существует no разница, что неверно. Там есть небольшая разница: классы быстрее.

С вызовом метода через класс имеется vtable с фиксированным смещением в классе, а указатель на реализацию желаемого метода находится на известном смещении внутри этой таблицы, поэтому переход к цели довольно просто. Однако, хотя класс может распространять только один суперкласс, класс может реализовать любое количество интерфейсов, поэтому вызовы методов через интерфейс сложнее. Для вызовов интерфейса он сначала ищет список интерфейсов класса, чтобы найти требуемый интерфейс, прежде чем он сможет найти реализацию метода в таблице этого интерфейса.

Когда используется более двух реализаций, производительность начинает расходиться.

Используются ли классы или интерфейсы, полиморфный вызов заставляет конвейер заподлицо на CPU, потому что CPU не может заранее увидеть цель перехода, и это имеет высокую стоимость. Когда сайт вызова известен во время выполнения, чтобы быть олигоморфным (oligo, что означает «несколько»), производительность резко возрастает, потому что хороший JVM обрабатывает эти случаи специально. Для мономорфного случая JVM может перейти непосредственно к методу единственной цели или даже встроить его. Для диморфного случая он реализует o.f();, как будто по (недействительный синтаксис): if (o.getClass() == A.class) A::f(o) else B::f(o);.

На самом деле я не уверен, почему диморфический случай, по-видимому, столь же быстр в вашем тестировании, как и мономорфный случай - разве предсказатель ветвления процессора не должен ошибаться в половине случаев на случайных данных? Может быть, есть и другие тонкости в работе там ...

Смотрите также:

+0

Я думаю, что CMOVcc вместо ветвления в биморфном случае. Учитывая, что случай 0 имеет довольно высокую задержку (mul + add, т. Е. 4 цикла), он оставляет достаточно времени для вычисления случая 1. Затем добавляется только один цикл для CMOVcc. Но я еще не смотрел на ас. – maaartinus

+0

@maaartinus Возможно, это просто тот случай, когда 1 быстрее, чем случай 0, потому что он не имеет умножения, поэтому средний случай выполняется быстрее, если вы делаете комбинацию случая 0 и case 1. – Boann

+0

Это тоже было бы возможно , но тогда будет половина штрафа в ветви, что, я думаю, будет больше весить. – maaartinus

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