C++ 在不产生动态查找成本的情况下,有一个通用的代码接口的好方法是什么?

C++ 在不产生动态查找成本的情况下,有一个通用的代码接口的好方法是什么?,c++,polymorphism,function-pointers,C++,Polymorphism,Function Pointers,我正在写一些处理数据的代码。用户可以选择若干组处理函数,然后将其应用于数据集。我希望在不同的地方实现所有这些组,但是因为它们都采用相同的参数,并且都做相似的事情,所以我希望它们有一个公共接口 作为一个优秀的C++程序员,我的第一个想法就是简单地使用多态性。只需使用所需的接口创建一些抽象类,然后从中派生出每组处理对象。然而,当我想到另一条皱纹时,我的希望很快破灭了。这些数据集非常庞大,导致所讨论的函数实际上被调用了数十亿次。据我所知,虽然动态查找相当便宜,但它比标准函数调用慢得多 我目前的想法是使

我正在写一些处理数据的代码。用户可以选择若干组处理函数,然后将其应用于数据集。我希望在不同的地方实现所有这些组,但是因为它们都采用相同的参数,并且都做相似的事情,所以我希望它们有一个公共接口

作为一个优秀的C++程序员,我的第一个想法就是简单地使用多态性。只需使用所需的接口创建一些抽象类,然后从中派生出每组处理对象。然而,当我想到另一条皱纹时,我的希望很快破灭了。这些数据集非常庞大,导致所讨论的函数实际上被调用了数十亿次。据我所知,虽然动态查找相当便宜,但它比标准函数调用慢得多

我目前的想法是使用函数指针,方式如下:

void dataProcessFunc1(mpz_class &input){...}
void dataProcessFunc2(mpz_class &input){...}
...
class DataProcessInterface
{
    ...
    void (*func1)(mpz_class);
    void (*func2)(mpz_class);
    ...
}
使用某种构造函数或其他东西来设置指针以指向正确的对象

所以我想我的问题是:这是一个好方法吗?还有别的办法吗?或者我应该学会停止担心并热爱动态查找吗?

虚拟函数调用是通过指针进行的函数调用。开销通常与通过指针的显式函数调用大致相同。换句话说,你的想法可能收获很少(很可能什么也得不到)

我的直接反应是从虚拟函数开始,当/如果探查器显示虚拟调用的开销变得显著时,我只担心其他事情


如果发生这种情况,另一种可能是在类模板中定义接口,然后将该接口的各种实现放入模板的专门化中。这通常消除了所有运行时开销(虽然通常是相当多的额外工作)。

< P>抽象接口方法从代码的角度来看是最干净的,而且最好用函数指针混淆代码,这是C++中的编程C。 您是否确实确定接口方法存在性能问题


最好先编写可读且可维护的代码,并且只有在需要时才进行优化。

我不同意上面的一个答案,即基于模板的解决方案可能会有最糟糕的开销或运行时。事实上,基于模板的解决方案允许编写更快的代码,无需使用虚拟函数或指针调用(不过,我同意,使用这些机制仍然不会带来显著的开销)

假设您使用一系列“特征”来配置处理接口,也就是说,可以由客户机配置以优化处理接口的处理部件或功能。假设一个类具有三个(参见示例)处理参数化:

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);
    }
};
这些电话没有开销。它们是普通调用,客户端可以使用
ProcessingInterface::process(data)配置处理

这仅在编译时知道不同的“方面”或“处理器”时适用,第一个示例就是这样

还请注意,您可以通过使用元编程工具(如库)编写更复杂的类,以包含更多的类、遍历它们等等

这些数据集非常庞大,导致所讨论的函数实际上被调用了数十亿次。据我所知,虽然动态查找相当便宜,但它比标准函数调用慢得多

几十亿次在多少时间里?如果你的应用程序运行一个小时,10亿次函数调用算不了什么,也不会影响性能。但如果整个数据集在100毫秒内处理,10亿个函数调用将是一个巨大的开销来源。简单地谈论一个函数被调用了多少次是没有意义的。就性能而言,重要的是它被调用的频率。每个时间单位的呼叫数

如果这实际上是一个性能问题,我会使用模板方法。用户不会在每次调用之间决定应用哪些操作。他会做一次决定,然后所有数十亿的电话都会被解决

只需为用户可以选择的每一组函数定义一个类,确保它们公开相同的接口(可能使用CRTP稍微简化流程并轻松地找出通用代码),然后根据用户选择的策略,将适当的类传递给(模板化的)负责执行所有处理的功能


但正如其他答案所说,这可能根本不是性能瓶颈。不要浪费时间去优化那些无关紧要的代码。

你能在硬件上运行一些测试,看看多态性是否是一个主要的性能降低因素吗?如果没有,那就采用更简洁的设计。你完全是在重新实现
virtual
dispatch。。。因此,与使用派生方法相比,您将获得完全相同的性能。模板解决方案的额外体积在某些情况下可能会使其性能更差,因为1)缓存约束2)现代处理器中计算GOTO的分支目标预测,其设计正是为了加速解释或面向对象代码的执行,这减少了动态解决方案的开销。使用虚拟函数听起来很合理(当然更容易)。我知道虚拟函数调用是通过指针调用的,但我担心的是,每次调用时它都必须查看一个表,以确定虚拟函数对应的子类。这是不是真的,或者根本不算什么?@Pascal:是的,这就是我建议先尝试虚拟函数的部分原因。另一部分是,,
class do_nothing
{
public:
    static void process(mpz_class&) {}
};