【问题标题】:Alternative/Suggestion for C++ Polymorphism With Virtual Function使用虚函数的 C++ 多态性的替代方案/建议
【发布时间】:2018-08-20 02:17:34
【问题描述】:

我有一个程序,它基本上可以计算 C++ 中一个非常大的向量的滚动平均值和其他滚动统计信息。对于大小为 N 的向量,我想获得另一个大小为 N 的向量,其中元素 k 包含从 0 到 k 的滚动统计信息。我将在下面使用滚动平均值进行说明。

该程序有一个对象,该对象具有根据新值和以前的统计值更新滚动统计信息的功能。例如:

inline float update(const float x) {
    _n++;
    _mean += (x - _mean)/_n;
    return _mean;
}

现在想支持其他的统计计算,比如滚动标准差,计划是添加一个纯虚类作为基类,带有纯虚阶跃函数,创建不同的子类对应不同的统计类型.

我的问题是:因为 step 函数将是该函数的主要部分并且将被执行数十亿次,我是否应该期望从内联函数切换到虚拟函数会显着降低速度?对于我想做的事情,还有其他更有效的选择吗?或者我没有使用正确的设计模式,还有其他更好的方法吗?

【问题讨论】:

  • 你不能总是保证得到内联代码,这取决于编译器。如果您打算更喜欢多态性,则会因为 vtable 而产生开销。
  • 像这样的虚拟单步更新程序不会赢得任何基准测试。不过,将更新循环作为虚函数的一部分是合理的。
  • 一般来说,虚拟函数的流行度和重要性都比以前要小得多。使用 lambda 二进制操作尝试 std::accumulate。

标签: c++ polymorphism


【解决方案1】:

据我了解,您可能会使用模板,类似于

template <typename Range, typename F>
auto f(const Range& r, F&& f)
{
    std::vector<std::decay_t<decltype(f(*r.begin()))>> result;

    result.reserve(r.size());
    for (const auto& e : v) {
        result.push_back(f(e));
    }
    return result;
}


class MeanCalculator
{
public:
    float operator()(float x) const {
        n++;
        mean += (x - mean) / n;
        return mean;
    }
private:
    std::size_t n = 0;
    float mean = 0.0f;
};

class VarianceCalculator
{
public:
    float operator()(float x) const {
        n++;
        // ...
    }
private:
    std::size_t n = 0;
    //...
};

然后

std::vector<float> numbers = /*...*/;
auto means = f(numbers, MeanCalculator{});
auto variances = f(numbers, VarianceCalculator{});

注意:std::transform不能使用,因为它不保证f的按序申请。

【讨论】:

  • 谢谢,这正是我需要的。我能说只要能提前知道类型,总是可以用模板代替虚函数吗?
  • 只要你不需要运行时调度,而是编译时(所以对于已知类型)一个,是的。
  • 因为我在这个多态中的每个类都有很多方法要实现,所以很难确保我不会忘记每个类的任何方法。你觉得做一个虚拟父类作为接口,让每个实现都成为这个虚拟类的子类,但永远不要使用运行时多态,是不是更好?
【解决方案2】:

使用多态性通常是一个非常好的策略。这是一种经过验证且经过时间考验的方法来构建正确且可维护的软件。 vtable 问题很少是影响性能的因素。一些有趣的文章herehere关于vtable的重度优化。

还有其他一些好主意。例如,查看Strategy Pattern。您可以在创建处理程序对象时使用它来选择您选择的函数,可能通过模板或作为 ctor 的参数。由于在构建时选择了一次策略,因此不会产生 vtable 惩罚。

另一个相关的想法是结合使用 lambda 函数和一些 std:: 算法。自 C++11 以来,这种模式已经变得相当普遍。 lambda 用于代替多态性。下面是一个简单的示例,它可能会为您的项目提供一些想法。

#include <iostream>
#include <vector>
#include <algorithm>

int main(int argc, char*argv[])
{
   std::vector<size_t> input = {2,4,6};
   std::vector<size_t> output;

   auto const operation = std::stoi(argv[1]);

   // transform the input vector to the output vector with algorithm selection
   switch(operation)
   {
     // just copy it
     case 1:  
     {
       std::for_each(input.begin() ,input.end(), [&](size_t element){
          output.push_back(element);
       });
       break;
     }

     // divide it by 2
     case 2:  
     {
       std::for_each(input.begin() ,input.end(), [&](size_t element){
          output.push_back(element/2);
       });
       break;
     }
   }

   for(auto const & element:output)
     std::cout << element << std::endl;
}

【讨论】:

  • “vtable 问题很少是影响性能的因素”。需要引用。
  • "// 复制就好了",所以简单output = input;.
  • 对于范围或std::transform 似乎比std::for_each 更合适。
  • @n.m.添加了一些引用
  • @Jarod42 这只是一个简单的例子,而不是原始问题的解决方案。尽管如此,你的 cmets 是准确的。
猜你喜欢
  • 2013-03-26
  • 2011-02-12
  • 1970-01-01
  • 1970-01-01
  • 2022-11-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多