Выбор между виртуальной функцией, function_pointer и функторами - PullRequest
2 голосов
/ 27 августа 2011

Я пишу класс, в котором реализация функции зависит от пользователей. В настоящее время я использую его как виртуальную функцию, и пользователям необходимо переопределить мой класс, чтобы обеспечить его реализацию. Я подумываю сделать его функтором (boost :: function) / function_pointer, чтобы пользователи могли на него зарегистрироваться. Приложение чрезвычайно критично к производительности, а скорость важнее, чем классы, которые выглядят хорошо. Может ли переход на функторы повысить производительность?

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

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

class Myclass
{
   public:
    registerFuncPtr(FuncPtrSignature);
    registerFunctor(boost::function func);
   private:    
   someMethod(someArgs)
   {
    ...
    if(_isFuncPtr)
        _func_ptr(args);
    else
        _functor(args);
    ...
   }
   bool _isFuncPtr;
   FuncptrSignature _func_ptr;
   boost::function _functor;
}

Обновление:

Я пишу разделяемую библиотеку, и клиенты будут динамически связываться с ней. Нет C ++ 0x. (

Ответы [ 3 ]

6 голосов
/ 27 августа 2011

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

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

template <typename Functor> void doProcessing( Functor f) {
   f( data ); 
}

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

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

  • принять указатель на функцию (способ C)

  • предоставить интерфейс (базовый класс) только с этой операцией (способ Java)

  • использоватьоболочка функтора (boost::function / std::function), которая выполняет стирание типа

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

Третий вариант является наиболее общим, поскольку он применяется стирание типа к пользователю вызываемый , и будетразрешить пользователям повторно использовать свой существующий код, используя функциональные адаптеры, такие как boost::bind или std::bind, а также лямбды, если их поддерживает компилятор.Это наиболее общий вариант и оставляет большинство вариантов выбора для пользователя.Однако с этим связана определенная стоимость: в стирании типа есть виртуальная отправка плюс дополнительный вызов функции для фактического фрагмента кода.

Обратите внимание, что если пользователю придется написать функцию для адаптации вашегоВ сочетании с их кодом стоимость этого созданного вручную адаптера, скорее всего, будет эквивалентна, поэтому, если они нуждаются в универсальности, тогда boost::function / std::function - путь.

С учетом разницы вЗатраты на альтернативы, скорее всего, в целом очень малы, и вопрос о том, будет ли царапать одну или две операции, будет зависеть от того, насколько замкнут цикл и насколько дорог пользовательский код.Если код пользователя займет пару сотен инструкций, вероятно, нет смысла не использовать самое общее решение.Если цикл запускается несколько тысяч раз в секунду, выцарапывание нескольких инструкций также ничего не изменит.Вернитесь назад, напишите реалистичный сценарий и проведите тестирование, и во время тестирования будьте в курсе того, что действительно важно для пользователя.Царапание нескольких секунд операции, которая занимает минуты, не стоит терять гибкости решений более высокого уровня.

1 голос
/ 27 августа 2011

Если вы после спектакля, используйте голые указатели функций.Если вам нужно состояние, предоставьте отдельный аргумент состояния типа void* в вашей функции и позвольте пользователям регистрировать свое состояние.Нет волшебства, которое могло бы сделать любую конструкцию C ++ более быстрой, чем эта, поскольку функторы, виртуальные функции и все остальное - всего лишь указатели на функции, заключенные в более или менее безопасный для типов конверт.

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

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

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

0 голосов
/ 27 августа 2011

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

...