Каков хороший способ иметь общий интерфейс с кодом без затрат на динамический поиск? - PullRequest
4 голосов
/ 08 февраля 2010

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

Будучи хорошим маленьким программистом на С ++, моей первой мыслью было просто использовать полиморфизм. Просто создайте некоторый абстрактный класс с желаемым интерфейсом и затем извлекайте из него каждый набор объектов обработки. Однако мои надежды быстро оправдались, когда я подумал о другой морщинке. Эти наборы данных огромны, в результате чего рассматриваемые функции вызываются буквально миллиарды раз. Хотя динамический поиск довольно дешев, насколько я понимаю, он намного медленнее, чем стандартный вызов функции.

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

void dataProcessFunc1(mpz_class &input){...}
void dataProcessFunc2(mpz_class &input){...}
...
class DataProcessInterface
{
    ...
    void (*func1)(mpz_class);
    void (*func2)(mpz_class);
    ...
}

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

Итак, я предполагаю, что мой вопрос таков: это хороший метод? Есть ли другой способ? Или я должен просто научиться перестать беспокоиться и любить динамический поиск?

Ответы [ 4 ]

7 голосов
/ 08 февраля 2010

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

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

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

3 голосов
/ 08 февраля 2010

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

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

template <typename Proc1, Proc2 = do_nothing, Proc3 = do_nothing>
struct ProcessingInterface
{
    static void process(mpz_class& element) {
        Proc1::process(element);
        Proc2::process(element);
        Proc3::process(element);
    }
};

Если у клиента разные «процессоры» со статической функцией «процесс», которые знают, как обрабатывать элемент, вы можете написать такой класс, чтобы «объединить» эти три обработки. Обратите внимание, что у класса do_nothing по умолчанию есть пустой метод процесса:

class do_nothing
{
public:
    static void process(mpz_class&) {}
};

У этих вызовов нет накладных расходов. Это обычные вызовы, и клиент может настроить обработку, используя ProcessingInterface<Facet1, Facet2>::process(data);.

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

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

2 голосов
/ 08 февраля 2010

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

Вы действительно определили, что у вас есть проблемы с производительностью с интерфейсным подходом?

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

1 голос
/ 08 февраля 2010

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

миллиарды раз сколько времени ? Если ваше приложение работает в течение часа, миллиард вызовов функций - ничто и не повлияет на производительность. Но если весь набор данных обрабатывается за 100 мс, миллиард вызовов функций являются значительным источником накладных расходов. Просто говорить о том, сколько раз вызывается функция, не имеет смысла. Что важно для производительности, так это то, как часто это называется. Количество звонков за единицу времени.

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

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

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

...