Накладные расходы на реализацию интерфейса

Один из моих коллег сказал мне, что внедрение интерфейсов представляет собой накладные расходы. Это правда?

Меня не интересуют микрооптимизации; Я просто хочу знать более глубокие детали, которые это влечет за собой.

Ответ 1

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

Участники:

Interface IFoo    defining a method
class Foo: IFoo   implements IFoo
class Bar         implements the same method as Foo, but no interface involved

поэтому я определил

Foo realfoo = new Foo();
IFoo ifoo = new Foo();
Bar bar =  new Bar();

и вызвал метод, который выполняет 20 конкатенаций строк, 10 000 000 раз для каждой переменной.

realfoo:   723 Milliseconds
ifoo:      732 Milliseconds
bar:       728 Milliseconds

Если метод ничего не делает, фактические вызовы выделяются немного больше.

  realfoo: 48 Milliseconds
  ifoo: 62 Milliseconds
  bar: 49 Milliseconds

Ответ 2

Говоря с точки зрения Java, по крайней мере в последних версиях Hotspot, ответ обычно мало или вообще не имеет накладных расходов, когда это имеет значение.

Например, предположим, что у вас есть такой метод, как:

public void doSomethingWith(CharSequence cs) {
  char ch = cs.charAt(0);
  ...
}

CharSequence - это, конечно, интерфейс. Таким образом, вы можете ожидать, что метод должен будет выполнять дополнительную работу, проверять тип объекта, находить метод и делать это, чтобы, возможно, искать интерфейсы в прошлом и т.д. И т.д. - по сути, все пугающие истории, которые вы могли себе представить...

Но на самом деле VM может быть намного умнее, чем это. Если это сработает, на практике вы всегда передаете объекты определенного типа, то не только он может пропустить проверку типа objec, но даже может встроить метод. Например, если вы вызываете метод, такой как приведенный выше, в цикле серии строк, то Hotspot может фактически встраивать вызов charAt(), так что получение символа в буквальном смысле становится несколькими инструкциями MOV, другими словами, вызов метода на интерфейсе может превратиться даже в вызов метода вообще. (P.S. Эта информация основана на сборочном выходе из версии отладки версии 1.6.)

Ответ 3

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

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

В подавляющем большинстве случаев преимущества использования вызовов виртуальных функций значительно уменьшают недостатки.

Ответ 4

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

Есть накладные расходы, но это накладные расходы на микро-оптимизацию. Например, интерфейс может делать много вызовов в IL-переключателе от вызова до callvirt, но это невероятно незначительное.

Ответ 5

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

Я занимаюсь сексом, но дело в том, что вам нужно найти свой баланс между понятным, понятным, поддерживаемым кодом и производительностью. Для 99.999% (повторяя, конечно) приложений, если вы помните, чтобы избежать ненужного повторения выполнения любого из ваших более дорогих методов, вы никогда не доберетесь до такой степени, что вам нужно сделать что-то сложнее для поддержания только для того, чтобы он работал быстрее.

Ответ 6

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

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

Кроме того, вам нечего мешать вам изменить контракт интерфейса, чтобы сделать вызов более грубым, например. IMyInterface.Process(Car[] cars) вместо IMyInterface.Process(Car car)

В Code Complete Стив МакКоннелл советует против постоянных микрооптимизаций. Он говорит, что лучше всего написать программу с использованием передовой практики (т.е. Сосредоточиться на ремонтопригодности, удобочитаемости и т.д.), А затем, как только вы закончите, если производительность недостаточно, профилируйте ее и предпримите шаги для устранения основных узких мест.

Нет смысла спекулятивно оптимизировать весь ваш код только потому, что он может быть быстрее. Если 80% вашего времени выполнения потрачено на выполнение 20% кода, очевидно, что безумие жертвовать свободной связью повсюду "только потому, что" оно может сбрить 10 микросекунд здесь или там ". Таким образом, вы сохраняете 10 микросекунд, но ваша программа не будет работать быстрее, если какая-то другая функция поглощает процессор.

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

Ответ 7

Накладные расходы по сравнению с чем?

Вызов через интерфейс дороже, чем вызов не виртуального метода, да. Я не тестировал его лично, но я думаю, что он похож по величине на виртуальный вызов.

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

Ответ 8

К сожалению, для Java еще есть определенная оптимизация, которая может быть выполнена для улучшения производительности интерфейса. Да, есть "почти нет накладных расходов" для invokevirtual и invokeinterface инструкций по сравнению с invokespecial, но есть проект Da Vinci, который нацелен на недостаток производительности в очень распространенное тривиальное использование интерфейсов: объект, который реализует только один интерфейс и никогда не перегружается.

Смотрите этот запрос Java bugparade для улучшения для всех технических деталей, которые вы могли бы пожелать.

Как всегда (и, похоже, вы это понимаете), проконсультируйтесь с Amdahl Law при обсуждении таких микро-оптимизаций. Если вы делаете так много вызовов методов и нуждаетесь в скорости, подумайте о рефакторинге в сочетании с тщательным бенчмаркингом.

Ответ 9

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

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

И все же это влияет на производительность. Я предполагаю, что это связано с метаданными/отражением, потому что, если вам не нужны метаданные, ТОЛЬКО время, в течение которого будет использоваться интерфейс, - это при отливке от менее специфического интерфейса к этому интерфейсу, а затем ему потребуется только быть тегом для проверки и посмотреть, возможно ли это.

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

Ответ 10

Из любопытства я внедрил небольшой тест. Код, согласно моей идее, должен блокировать JVM из результатов разрешения метода кэширования и таким образом показывать явное различие между invokeinterface и invokevirtual в Java.

Результат теста состоит в том, что invokeinterface на 38% медленнее, чем invokevirtual.

Ответ 11

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

Сказав, что недавно я реорганизовал несколько классов, представив интерфейсы и выполнив все вызовы через интерфейс. Я был настолько уверен (ленив), что это не повлияет на то, что мы выпустили его без проверки производительности. Оказалось, что это повлияло на производительность всего приложения на 10% (а не только на звонки). Мы сделали ряд изменений, и это было последнее, что мы подозревали. В конце концов, когда мы вернулись к конкретным классам, первоначальная производительность была восстановлена.

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

Ответ 12

Отправка виртуального буфера отличается от диспетчера интерфейса. Vance Morrison, руководство CLR JIT описывает это подробно в этом сообщении в блоге. http://blogs.msdn.com/vancem/archive/2006/03/13/550529.aspx