виртуальное или типовое литье - PullRequest
2 голосов
/ 24 декабря 2011

У меня есть вопрос.Я пытаюсь создать код для обновления своей игры, но я попал в "дилемму".Я не хочу использовать виртуальный, и единственная причина для этого - ВСЕ, с кем я разговаривал (на форумах, в чатах, в друзьях), говорят, что виртуалы делают код очень медленным, поэтому я провел исследование и выяснил, что поиск выполняется изvtable может снизить производительность почти вдвое.Итак, я использую его для задач, которые не нужно обновлять каждый кадр.Все работало нормально, пока я не попал в функции обновления / рендеринга.Тогда я начал думать, чтобы найти обходной путь.Есть идея, но сначала я хотел бы спросить людей, которые имеют знания об этом, прежде чем ее реализовать.

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

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

void GraphicsSubsystem::renderScene(Subsystem * subsystem, Event * event) {
   GraphicsSubsystem * graphics = static_cast<GraphicsSubsystem *>(subsystem);
   graphics->renderScene();
}

void ScriptingSubsystem::runLine(Subsystem * subsystem, Event * event) {
   ScriptingSubsystem * scripting = static_cast<ScriptingSubsystem *>(subsystem);
   Event1<String> * e = static_cast<Event1<String> *>(event);
   scripting->runLine(e->getArg());
}

Аргументами всегда являются класс абстрактной подсистемы и класс базового события.Функция runLine У меня нет проблем приведения, потому что я не запускаю строку кода в каждом кадре.Однако функция renderScene делает меня немного неудобным.

tl; dr Итак, вот мой вопрос.Статическое приведение объекта в каждом кадре быстрее, чем вызов виртуальной функции в каждом кадре?

Ответы [ 4 ]

6 голосов
/ 24 декабря 2011

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

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

3 голосов
/ 24 декабря 2011

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

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

@ Комментарий Джири, безусловно, правильный, но единственный способ узнать это - это измерить его - эффекты, о которых мы здесь говорим, очень специфичны для умения компилятора / оптимизатора / процессора. Построение тестов не займет много времени.

3 голосов
/ 24 декабря 2011

Общий ответ - это зависит от конкретных обстоятельств.

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

Для меня неясно, почему это должно быть медленнее, чем ваше статическое приведение.

На вашем месте я бы создал прототип кода / заглушки, используя оба метода, вызвал его миллион раз и измерил производительность.

1 голос
/ 25 декабря 2011

Если вам нужны функции виртуальных функций, просто используйте их.Они там, потому что они полезны.

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

Доверяйте своему компилятору!

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...