В своем дизайне классов я широко использую абстрактные классы и виртуальные функции. Было ощущение, что виртуальные функции влияют на производительность. Это правда? Но я думаю, что эта разница в производительности незаметна и, похоже, делаю преждевременную оптимизацию. Правильно?
c++
performance
optimization
virtual-functions
Navaneeth KN
источник
источник
Ответы:
Хорошее практическое правило:
Использование виртуальных функций очень незначительно повлияет на производительность, но вряд ли повлияет на общую производительность вашего приложения. Лучше искать улучшения производительности в алгоритмах и вводе-выводе.
Отличная статья о виртуальных функциях (и многом другом) - это указатели на функции-члены и самые быстрые из возможных делегатов C ++ .
источник
Ваш вопрос вызвал у меня любопытство, поэтому я продолжил и проверил некоторые тайминги на процессоре PowerPC 3 ГГц в порядке, с которым мы работаем. Тест, который я провел, заключался в создании простого 4d векторного класса с функциями get / set.
Затем я установил три массива, каждый из которых содержит 1024 этих вектора (достаточно малых, чтобы поместиться в L1), и запустил цикл, в котором они добавлялись друг к другу (Ax = Bx + Cx) 1000 раз. Я побежал это с функциями , определенными в качестве
inline
,virtual
и регулярные вызовы функций. Вот результаты:Таким образом, в этом случае (когда все помещается в кеш) вызовы виртуальных функций были примерно в 20 раз медленнее, чем встроенные вызовы. Но что это на самом деле означает? Каждое прохождение цикла вызывало в точности
3 * 4 * 1024 = 12,288
вызовы функций (1024 вектора, умноженные на четыре компонента, умноженные на три вызова на добавление), поэтому это время представляет1000 * 12,288 = 12,288,000
вызовы функций. Виртуальный цикл занял на 92 мс больше, чем прямой цикл, поэтому дополнительные накладные расходы на вызов составляли 7 наносекунд на функцию.Из этого я делаю вывод: да , виртуальные функции намного медленнее, чем прямые функции, и нет , если вы не планируете вызывать их десять миллионов раз в секунду, это не имеет значения.
См. Также: сравнение сгенерированной сборки.
источник
Когда Objective-C (где все методы виртуальные) является основным языком для iPhone, а чертовски Java - основным языком для Android, я думаю, что довольно безопасно использовать виртуальные функции C ++ на наших двухъядерных башнях с частотой 3 ГГц.
источник
В приложениях с очень высокой производительностью (например, видеоиграх) вызов виртуальной функции может быть слишком медленным. На современном оборудовании самая большая проблема производительности - это отсутствие кэша. Если данных нет в кеше, могут пройти сотни циклов, прежде чем они станут доступны.
Обычный вызов функции может вызвать промах кэша инструкций, когда ЦП выбирает первую инструкцию новой функции, а ее нет в кэше.
Для вызова виртуальной функции сначала необходимо загрузить указатель vtable из объекта. Это может привести к пропуску кэша данных. Затем он загружает указатель на функцию из vtable, что может привести к еще одному промаху кэша данных. Затем он вызывает функцию, которая может привести к пропуску кэша инструкций, как невиртуальная функция.
Во многих случаях два дополнительных промаха в кэше не являются проблемой, но в жестком цикле критического для производительности кода это может резко снизить производительность.
источник
Со страницы 44 руководства Агнера Фога «Оптимизация программного обеспечения на C ++» :
источник
switch
.case
Конечно, с абсолютно произвольными значениями. Но если всеcase
s являются последовательными, компилятор мог бы оптимизировать это в таблицу переходов (ах, это напоминает мне старые добрые дни Z80), которая должна быть (за отсутствием лучшего термина) постоянным временем. Не то чтобы я рекомендовал пытаться заменить vfuncs наswitch
, что абсурдно . ;)абсолютно. Это было проблемой еще тогда, когда компьютеры работали на частоте 100 МГц, так как каждый вызов метода требовал поиска в vtable перед его вызовом. Но сегодня ... на процессоре с тактовой частотой 3 ГГц, который имеет кэш 1-го уровня с большим объемом памяти, чем был у моего первого компьютера? Не за что. Выделение памяти из основной ОЗУ будет стоить вам больше времени, чем если бы все ваши функции были виртуальными.
Это похоже на старые, старые времена, когда люди говорили, что структурное программирование было медленным, потому что весь код был разделен на функции, каждая функция требовала выделения стека и вызова функции!
Единственный раз, когда я бы даже подумал о том, чтобы подумать о влиянии виртуальной функции на производительность, - это если бы она очень интенсивно использовалась и создавалась в шаблонном коде, который в конечном итоге повсеместно использовался. Даже тогда я бы не стал тратить на это слишком много усилий!
PS подумайте о других «простых в использовании» языках - все их методы скрыты виртуально, и в настоящее время они не сканируются.
источник
Помимо времени выполнения, есть еще один критерий производительности. Vtable также занимает место в памяти, и в некоторых случаях этого можно избежать: ATL использует имитацию динамической привязки во время компиляции. " во с шаблонами.получить эффект «статического полиморфизма», который сложно объяснить; вы в основном передаете производный класс в качестве параметра в шаблон базового класса, поэтому во время компиляции базовый класс «знает», какой у него производный класс в каждом экземпляре. Не позволит вам хранить несколько различных производных классов в коллекции базовых типов (это полиморфизм во время выполнения), но из статического смысла, если вы хотите создать класс Y, который будет таким же, как уже существующий класс шаблона X, который имеет ловушки для такого переопределения, вам просто нужно переопределить методы, которые вам нужны, и тогда вы получите базовые методы класса X без необходимости иметь vtable.
В классах с большими объемами памяти стоимость одного указателя vtable невелика, но некоторые классы ATL в COM очень малы, и это стоит экономии vtable, если случай полиморфизма времени выполнения никогда не произойдет.
См. Также этот другой вопрос SO .
Кстати, вот сообщение, которое я нашел, в котором говорится об аспектах производительности процессора.
источник
Да, вы правы, и если вам интересно узнать о стоимости вызова виртуальной функции, этот пост может показаться вам интересным.
источник
Единственный способ увидеть, что виртуальная функция станет проблемой с производительностью, - это если многие виртуальные функции будут вызываться в жестком цикле, и тогда и только тогда, когда они вызовут сбой страницы или другую «тяжелую» операцию с памятью.
Хотя, как и другие люди, это никогда не будет проблемой для вас в реальной жизни. И если вы думаете, что это так, запустите профилировщик, проведите несколько тестов и проверьте, действительно ли это проблема, прежде чем пытаться «изменить дизайн» вашего кода для повышения производительности.
источник
Когда метод класса не является виртуальным, компилятор обычно выполняет встраивание. Напротив, когда вы используете указатель на некоторый класс с виртуальной функцией, реальный адрес будет известен только во время выполнения.
Это хорошо видно на тесте, разница во времени ~ 700% (!):
Воздействие вызова виртуальной функции сильно зависит от ситуации. Если внутри функции мало вызовов и много работы - им можно пренебречь.
Или, когда это виртуальный вызов, который многократно используется много раз при выполнении какой-то простой операции - он может быть действительно большим.
источник
++ia
. Ну и что?Я возвращался к этому по крайней мере 20 раз в моем конкретном проекте. Хотя можно добиться больших успехов с точки зрения повторного использования кода, ясности, удобства обслуживания и удобочитаемости, с другой стороны, снижение производительности все еще с виртуальными функциями существуют проблемы с производительностью.
Будет ли падение производительности заметно на современном ноутбуке / настольном компьютере / планшете ... вероятно, нет! Однако в некоторых случаях со встроенными системами снижение производительности может быть движущим фактором неэффективности вашего кода, особенно если виртуальная функция вызывается снова и снова в цикле.
Вот несколько устаревшая статья, в которой анализируются передовые практики для C / C ++ в контексте встроенных систем: http://www.open-std.org/jtc1/sc22/wg21/docs/ESC_Boston_01_304_paper.pdf
В заключение: программист должен понять плюсы и минусы использования одной конструкции над другой. Если вы не стремитесь к сверхпроизводительности, вы, вероятно, не заботитесь о снижении производительности и должны использовать все аккуратные OO-вещи в C ++, чтобы сделать свой код максимально удобным для использования.
источник
По моему опыту, самое главное - это возможность встроить функцию. Если у вас есть потребности в производительности / оптимизации, которые диктуют, что функция должна быть встроена, вы не можете сделать функцию виртуальной, потому что это предотвратит это. В противном случае вы, вероятно, не заметите разницы.
источник
Следует отметить, что это:
может быть быстрее, чем это:
Это связано с тем, что первый метод вызывает только одну функцию, а второй может вызывать множество разных функций. Это применимо к любой виртуальной функции на любом языке.
Я говорю «может», потому что это зависит от компилятора, кеша и т. Д.
источник
Ухудшение производительности при использовании виртуальных функций никогда не может перевесить преимущества, которые вы получаете на уровне дизайна. Предположительно, вызов виртуальной функции будет на 25% менее эффективным, чем прямой вызов статической функции. Это связано с тем, что существует уровень косвенного обращения через VMT. Однако время, затрачиваемое на выполнение вызова, обычно очень мало по сравнению со временем, затрачиваемым на фактическое выполнение вашей функции, поэтому общие затраты на производительность будут незначительными, особенно с текущей производительностью оборудования. Кроме того, компилятор иногда может оптимизировать и видеть, что виртуальный вызов не требуется, и скомпилировать его в статический вызов. Так что не беспокойтесь, используйте столько виртуальных функций и абстрактных классов, сколько вам нужно.
источник
The performance penalty of using virtual functions can sometimes be so insignificant that it is completely outweighed by the advantages you get at the design level.
«Ключевое различие в томsometimes
, что нет»never
.Я всегда сомневался в этом, тем более, что - несколько лет назад - я также провел такой тест, сравнивая тайминги стандартного вызова метода члена с виртуальным, и был очень зол на результаты в то время, поскольку пустые виртуальные вызовы были В 8 раз медленнее невиртуальных.
Сегодня мне нужно было решить, использовать ли виртуальную функцию для выделения большего объема памяти в моем буферном классе в приложении, очень важном для производительности, поэтому я погуглил (и нашел вас) и, в конце концов, снова провел тест.
И был очень удивлен, что на самом деле это больше не имеет никакого значения. Хотя имеет смысл иметь встроенные файлы быстрее, чем невиртуальные, и они быстрее виртуальных, часто возникает общая нагрузка на компьютер, независимо от того, есть ли в вашем кеше необходимые данные или нет, и хотя вы можете оптимизировать на уровне кеша, я думаю, это должны делать разработчики компилятора больше, чем разработчики приложений.
источник