Хранение шаблонных объектов C ++ как одного типа - PullRequest
1 голос
/ 08 января 2011

У меня есть класс, который является ключевым компонентом чувствительного к производительности пути кода, поэтому я стараюсь максимально его оптимизировать.Раньше класс был:

class Widget
{
    Widget(int n) : N(n) {}
    .... member functions that use the constant value N ....
    const int N;                // just initialized, will never change
}

Аргументы конструктора известны во время компиляции, поэтому я изменил этот класс на шаблон, чтобы N можно было скомпилировать в функции:

template<int N>
class Widget
{
   .... member functions that use N ....
}

У меня есть другой класс с методом:

Widget & GetWidget(int index);

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

ТАК, мой вопрос такой:

Я довольноуверен, что я хочу лучшее из обоих миров (время компиляции / время выполнения), и это может оказаться невозможным.Но есть ли способ повысить производительность, зная N во время компиляции, но все еще имея возможность возвращать виджеты того же типа?

Спасибо!

Ответы [ 5 ]

8 голосов
/ 08 января 2011

Проблема здесь в том, что если вы храните виджеты одного и того же типа, то код, который извлекает виджеты из этого хранилища (вызывая GetWidget) не не знает N во время компиляции [*] , Код, который вызывает конструктор, знает N, но код, который использует объект, должен справиться с несколькими возможностями.

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

Может быть, что виртуальный вызов функции, реализованной в шаблоне вашего класса, быстрее, чем не виртуальный вызов функции, которая использует N, не зная значения:

class Widget {
  public:
    virtual ~Widget() {}
    virtual void function() = 0;
};

template <int N>
class WidgetImpl : public Widget {
  public:
    virtual void function() { use N; }
};

Оптимизатор, вероятно, может выполнять свою работу лучше всего, когда известно N, поскольку он может оптимально развернуть циклы, преобразовать арифметику и т. Д. Но с виртуальным вызовом вы начинаете видеть один большой недостаток, заключающийся в том, что ни один из вызовов не может быть встроенным (и я предполагаю, что виртуальный вызов будет менее вероятным, чем не виртуальный, если он не встроен). ). Выигрыш от встраивания с неизвестным N может быть больше, чем выигрыш от знания N, или может быть меньше. Попробуйте их обоих и посмотрите.

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

switch(n) {
    case 1: /* do something using 1 */; break;
    case 2: /* do the same thing using 2 */; break;
    default: /* do the same thing using n */; break;
};

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

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

[*] Если вызывающий код знает значение N во время компиляции, тогда вы можете заменить GetWidget на шаблон функции, подобный этому:

template <int N>
Widget<N> &getWidget(int index) {
    return static_cast<Widget<N> &>(whatever you have already);
}

Но я предполагаю, что звонящий не знает, потому что, если бы он знал, вы, вероятно, не спросили бы ...

2 голосов
/ 08 января 2011

Если ваши типы конечны и известны , вы можете использовать boost::variant в качестве аргумента вашего конструктора.

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

вот какой-то псевдокод

boost::variant< int, double, std::string > variant;
const variant foo( 1 );
const variant bar( 3.14 );
const variant baz( "hello world" );

const Widget foo_widget( foo );
const Widget bar_widget( bar );
const Widget baz_widget( baz );

Кроме того, вы можете использовать boost::any для большей гибкости .

2 голосов
/ 08 января 2011

Я думаю, что следующее не вариант?

template <int N>
Widget<N> & GetWidget();

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

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

2 голосов
/ 08 января 2011

Вам необходимо объявить тип без шаблонов, от которого наследуется шаблонный тип, а затем сохранить виджеты как указатели на базовый класс без шаблонов.Это единственный (безопасный для типов) способ выполнить то, что вы ищете.

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

1 голос
/ 08 января 2011

Вы можете написать шаблонную функцию GetWidget.Для этого вам потребуется знать тип при вызове GetWidget:

w = GetWidget<Box>(index);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...