Основываясь на некоторых вопросах и ваших ответах в комментариях, вот несколько соображений.
1) Ваша проблема (если она есть, ваше решение может быть уже близко к оптимальному, в зависимости от детали, которые вы не упомянули), скорее всего, где-то еще, не в накладных расходах при вызове виртуальной функции.
Если вы действительно выполняете это в жестком l oop, и в реализациях мало что происходит f (), который затрагивает много памяти, ваши vtables, вероятно, остаются в кеше L1, и издержки виртуальных функций будут абсолютно минимальными, если таковые имеются, на современном оборудовании.
2) Вы говорите: Сами функции f () очень просты, например, одна из них просто умножает значения по двум адресам памяти и сохраняет продукт по третьему адресу »- это может быть не так невинно, как вы ожидаете. Для справки, переход к кэш-памяти L1 обойдется вам примерно в 3 цикла, а доступ к ОЗУ может стоить до 60-200, в зависимости от вашего оборудования.
Если у вас достаточно этих объектов (чтобы сохранить все память, на которую они ссылаются в кеше L1, невозможна), и области памяти, на которые они ссылаются, в основном случайны (так что предварительная выборка неэффективна), и / или вы касаетесь достаточного количества вещей в остальной части вашей программы (чтобы все соответствующие данные получали освобождается из кэша между циклами вашего вектора), стоимость извлечения и хранения значений из памяти и в нее / более низкие уровни кэша в худшем случае перевесят стоимость вызовов виртуальных функций на порядки.
3) Вы перебираете вектор указателей на объекты, а не на сами объекты.
В зависимости от того, как вы распределяете объекты и насколько они велики, это может не быть проблемой - предварительная выборка будет творить чудеса для Вы, если вы распределяете их в узком l oop, а ваш распределитель упаковывает их красиво у. Однако, если вы распределяете / освобождаете много других вещей и смешиваете распределение этих объектов между ними, они могут в конечном итоге располагаться редко и в основном в случайных местах в памяти; затем итерирование по ним в порядке создания потребует большого количества случайных чтений из памяти, что снова будет намного медленнее, чем издержки любой виртуальной функции.
4) Вы говорите «вызовы f () для вектора дети должны быть в порядке "- не так ли?
Если они это сделают, то вам в некотором смысле не повезло. Однако, если вы можете перестроить вашу систему так, чтобы их можно было упорядочивать по типу, тогда можно получить большую скорость в различных аспектах - вы, вероятно, могли бы выделить массив для каждого типа объекта (хороший, плотный в памяти), перебирайте их по порядку (удобен для предварительной выборки) и вызывайте ваши f () в группах для одного, хорошо известного типа (дружественный встраивание, дружественный кэш инструкций).
5) И наконец - если ни один из вышеперечисленных случаев не применим, и ваша проблема действительно заключается в вызовах виртуальных функций (маловероятно), тогда, да, вы можете попытаться сохранить указатель на точную функцию, которую вы должны вызывать для каждого объекта каким-либо образом - либо вручную, либо используя один из методов стирания типа / стирания / утки, которые предлагали другие.
Мой главный вопрос заключается в следующем - при изменении архитектуры вашей системы можно получить много преимуществ.
Помните: доступ к вещам, которые уже находятся в кеше L1 / L2 - это хорошо, необходимо go к L3 / RAM для данных хуже; доступ к памяти в последовательном порядке - это хорошо, перепрыгивание через память - плохо; вызов одного и того же метода в узком l oop, потенциально его вставка, это хорошо, вызов большого количества различных методов в узком l oop хуже.
Если это часть вашей программы, производительность которого действительно имеет значение, вы должны рассмотреть возможность изменения архитектуры вашей системы, чтобы учесть некоторые из ранее упомянутых оптимизаций. Я знаю, что это может показаться пугающим, но в эту игру мы играем. Иногда вам нужно пожертвовать «чистыми» OOP и абстракциями ради производительности, если проблема, которую вы решаете, позволяет это сделать.