在虚函数、函数指针和函子之间进行选择

发布于 2024-12-01 20:02:27 字数 734 浏览 3 评论 0原文

我正在编写一个类,其中函数的实现之一取决于用户。目前,我将其作为虚拟函数,用户需要重写我的类才能提供其实现。我正在考虑将其设为仿函数(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。 :(

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.
Application 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. :(

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

何止钟意 2024-12-08 20:02:27

这取决于您的特定用例以及用户添加代码的方式。以下是纯粹的理论分析,但您应该在实际决定之前构建一个现实的场景并测试性能。

如果要在编译时添加代码(即您向用户提供代码,他们创建逻辑并将所有内容一起编译),那么最好的方法可能是提供一个采用 Functor 的模板类型参数。

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

优点是编译器可以访问整个代码,这意味着它可以就是否内联代码做出最佳决策,并且在内联的情况下可能能够进一步优化。缺点是您需要使用每个新逻辑片段重新编译程序,并且必须将产品与用户扩展一起编译,但很多时候这是不可能的。

如果要在主产品编译之后执行扩展,即客户可以生成自己的扩展并将其用于编译后的可执行文件(例如插件),那么您应该考虑替代方案:

  • 接受函数指针(C 方式)

  • 提供仅具有该操作的接口(基类)(Java 方式)

  • 使用执行类型擦除的函子包装器 (boost::function/std::function)

这些是按纯粹性能排序的。 C 方式的成本是三种方式中较小的一个,但在大多数情况下与接口选项的差异可以忽略不计(每个函数调用有一个额外的间接),并且它为您提供了在调用对象中保留状态的选项(这将有通过第一个选项中的全局状态来完成)。

第三个选项是最通用的,因为它将类型擦除应用于用户可调用,并允许用户通过使用重用其现有代码函数适配器,例如 boost::bindstd::bind 以及 lambda(如果编译器支持)。这是最通用的,给用户留下了最多的选择。不过,它有一个相关的成本:类型擦除中有一个虚拟调度,加上对实际代码段的附加函数调用。

请注意,如果用户必须编写一个函数来使您的界面与他们的代码相适应,那么手动制作的适配器的成本很可能是相同的,因此如果他们需要通用性,那么 boost::function/std::function 是正确的方法。

至于替代方案的成本差异,它们总体上很可能非常,并且刮擦一两个操作是否重要将取决于循环的紧密程度以及用户代码的昂贵程度。如果用户代码需要执行数百条指令,那么不使用最通用的解决方案可能没有意义。如果循环每秒运行几千次,那么划几条指令也不会产生任何影响。返回,编写一个现实场景并进行测试,并在测试时了解对用户真正重要的内容。在需要几分钟的操作中浪费几秒钟的时间是不值得的,因为这样会失去更高级别解决方案的灵活性。

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.

追星践月 2024-12-08 20:02:27

如果您追求性能,请使用裸函数指针。如果您需要状态,请在函数中提供一个 void* 类型的单独状态参数,并让用户注册其状态。没有任何魔法可以使任何 C++ 构造比这更快,因为函子和虚函数以及所有其他函数都只是包装在或多或少类型安全信封中的函数指针。

更重要的是,在谈论优化时,除了分析器之外,您不应该相信任何东西(顺便说一句,这包括上面的段落)。

内联理论上可以提高性能,但用户的函数无法内联(编译器无法内联尚未编写的代码)。如果您不编译库,而是将其作为源代码分发,则内联可能会带来一些性能优势。与往常一样,只有分析器才能判断。

函数对象可能优于函数指针的一种情况是将它们传递给函数模板。如果模板本身没有内联,则函数对象可能会在模板实例化内部内联,而函数指针则不会。

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.

老娘不死你永远是小三 2024-12-08 20:02:27

编译器可以轻松地内联 Functor 调用,而不是通过函数指针调用函数,所以是的,更喜欢 Functor 而不是函数指针。

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

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文