【问题标题】:c++ performance of returning primitive by value or by (const) reference通过值或(const)引用返回原语的c ++性能
【发布时间】:2010-11-11 22:41:26
【问题描述】:

假设以下玩具类和现代编译器(例如最近的 gcc)。

template <typename T>
class SomeVec {
public:
  ...
  virtual T get(const int index) = 0;
}

该应用程序涉及基于存储在 SomeVec 子类中的值的大量数字运算,T 是原始类型,例如 int。但是stl containersboost::numeric::ublas::vector 的做法是通过(const) reference 返回存储的值。

我想知道这涉及的性能差异。在this question 中显示,按值访问数组元素和按引用访问向量元素会产生相同的代码,因此我假设编译器在某些情况下可以优化东西。

现在我的问题是:

  • (1) stlublas 是模板化的,而我的解决方案需要虚拟方法。这会阻碍现代编译器优化代码的能力吗?

  • (2) 如果编译器无法优化以将 const 原子引用作为值返回,那么我是否认为虚拟方法调用和取消引用成本大致相同是正确的?还是一个比另一个贵很多?

谢谢!

【问题讨论】:

  • 你这里有一个纯虚方法,意味着你有派生类。你能举个例子吗?
  • @Oli:子类可以是多种多样的,但例如一个简单的实现将有一个内部向量或映射并返回一个存储的值。当然,这比返回类型所带来的开销要多得多,但我们不要考虑这一点。

标签: c++ optimization reference return-type


【解决方案1】:

STL 返回引用的原因是因为模板化代码无法知道返回的对象很小。虽然int 没有问题,但返回一个大结构会无缘无故减慢速度。在后一种情况下,仅使用引用才有意义,并且由于合理的编译器可以优化使用原始类型的情况,您也可以在整个过程中使用引用。

请注意,您的方法 virtual T get(const int index) 在其他方面与 STL 容器方法不同。最重要的是,与上述问题相关的是,您的方法返回索引对象的副本:操作结果不会改变容器中对象的状态。

此外,将 index 参数声明为 const 没有任何作用,因为您通过值传递 index,所以您所做的只是防止自己在实现中本地更改 index。如果您通过引用传递 index,那将是另一回事,但您应该 be wary 这样做。

最后,你确定你的类需要动态多态(即,有虚方法)吗? STL 容器被有意设计为不被继承(这就是它们没有virtual 析构函数的原因)。容器并不是为了为派生类提供接口,而是为了方便实现。我认为您建议的子类示例可以很容易地实现为围绕模板化容器的包装类,有利于通过组合而不是继承来重用代码(Gang of Four 等提倡的)。除了良好的实践之外,避免使用virtual 方法可以节省对象中的 vtable 和相应指针,并且在每次调用中都需要额外的 vtable 查找。如果您真的不需要动态多态性,为什么要付出代价(并且可能会阻止编译器优化)?

【讨论】:

    【解决方案2】:

    在模板类中有虚函数是不寻常的。如果函数不是虚函数,编译器通常会内联代码并将按引用返回和按值返回之间的差异优化到无。

    如果函数不是通过指针或引用调用,编译器可能仍会内联该函数 - 编译器将知道在这种情况下要调用的确切成员函数,并且不需要通过 vtable 查找它。

    引用的开销很小,只需一次取消引用。它甚至可能不是汇编级别的完整指令。

    【讨论】:

    • 我明白了,谢谢。在我的情况下,将需要 vtable 查找。至于template+virtual,不同的包含类型需要template,而通过virtual抽象是因为容器元素的获取方式可以多种多样,应该对用户隐藏。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-03
    • 1970-01-01
    • 2012-05-25
    • 1970-01-01
    • 1970-01-01
    • 2020-02-28
    相关资源
    最近更新 更多