开发者

Choosing between virtual function, function_pointer and functors

开发者 https://www.devze.com 2023-03-31 08:29 出处:网络
I am writing a class in which a one of the function\'s implementation depends on the users. Currently I have it as a virtual function and users need to override my class to provide its implementation.

I am writing a class in which a one of the function's implementation depends on the users. Currently I have it as a virtual function and users need to override my class to provide its implementation. I am thinking of making it a functor(boost::function)/function_pointer so that users can register to it. Applicati开发者_如何学运维on is extremely performance critical and speed is more important than the classes looking nice. Is changing to functors give some performance benefit?

In most cases, it will be a free function so a function pointer should be fine. But I guess there would be some cases where state may be required and hence it needs to be a functor.

Will I get any preformance benefit by allowing to register either a function_ptr or a functor and calling the appropriate one based on some bool? Something similar to this.

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;
}

Update:

I am writing a shared library and the clients would dynamically link against it. No C++0x. :(


This depends on your particular use case and how the users add their code. The following is a purely theoretical analysis, but you should build a realistic scenario and test the performance before actually deciding.

If the code is to be added at compile time (i.e. you provide the code to the users, they create their logic and compile everything together) then probably the best approach is to provide a template that takes a Functor type argument.

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

The advantage is that the compiler has access to the whole code, and that means that it can take the best decisions as to whether it inlines or not the code, and might be able to optimize further in the case of inlining. The disadvantage is that you need to recompile the program with each new piece of logic and that you have to compile together your product with the user extensions, with many times it not possible.

If the extensions are to be performed after compilation of your main product, i.e. clients can produce their own extensions and use them to a compiled executable (think plugins) then you should consider the alternatives:

  • accept a function pointer (the C way)

  • provide an interface (base class) with just that operation (the Java way)

  • use a functor wrapper (boost::function/std::function) that performs type erasure

These are ordered by pure performance. The cost of the C way is the smaller of the three, but the difference with the interface option is negligible in most cases (an extra indirection per function call) and it buys you the option of keeping state in the calling object (this would have to be done through global state in the first option).

The third option is the most generic as it applies type-erasure to the user callable, and will allow users to reuse their existing code by using function adaptors like boost::bind or std::bind as well as lambdas if the compiler supports them. This is the most generic and leaves the most choices to the user. There is an associated cost to it though: there is a virtual dispatch in the type erasure plus an additional function call to the actual piece of code.

Note that if the user will have to write a function to adapt your interface with their code, the cost of that manually crafted adaptor will most probably be equivalent, so if they need the genericity, then boost::function/std::function are the way to go.

As of the difference in costs of the alternatives, they are most probably very small overall, and whether scratching one or two operations matters will depend on how tight the loop is and how expensive the user code is. If the user code is going to take a couple hundred instructions, there is probably no point in not using the most generic solution. If the loop is run a few thousand times per second, scratching a few instructions will not make a difference either. Go back, write a realistic scenario and test, and while testing be aware of what really matters to the user. Scratching a few seconds of an operation that takes minutes is not worth loosing the flexibility of the higher level solutions.


If you are after performance, go with naked function pointers. If you need state, provide a separate state argument of type void* in your function, and let the users register their state. There's no magic that can make any C++ construct to be faster than that, since functors and virtual functions and all the rest are all just function pointers wrapped in a more or less type-safe envelope.

More importantly, when talking about optimization, you should no trust anything whatsoever but your profiler (this includes the paragraph above BTW).

Inlining could in theory improve the performance, but the users' functions cannot be inlined (a compiler cannot inline code that hasn't been written yet). If you don't compile your library, but distribute it as source code, there's a possibility of some performance benefit due to inlining. As always, only a profiler can tell.

One case when a function object is likely to outperform a function pointer is passing them to a function template. If the template itself is not getting inlined, then the function object is likely to get inlined inside the template instantiation, and the function pointer is not.


Compiler can and easily inlines Functor calls rather than calls to functions through function pointers So yes prefer a Functor over function pointer.

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号