代码之家  ›  专栏  ›  技术社区  ›  ron

通过值或(const)引用返回原语的C++性能

  •  2
  • ron  · 技术社区  · 14 年前

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

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

    这个应用程序涉及到基于存储在somevec子类中的值的相当数量的数字处理,例如t是一个原始类型。 int . 然而,实践 stl containers boost::numeric::ublas::vector 是通过返回存储值 (const) reference .

    我想知道这涉及到的性能差异。在 this question 结果表明,数组元素按值访问和矢量元素按引用访问的结果是相同的代码,所以我假设编译器在某些情况下可以优化东西。

    现在我的问题是:

    • (1) stl ublas 是模板化的,而我的解决方案需要虚拟方法。这是否妨碍了现代编译器优化代码的能力?

    • (2)如果编译器不能优化返回常量原子引用作为值,那么我是否认为虚拟方法调用和取消引用的成本大致相同?或者一个比另一个贵得多?

    谢谢!

    2 回复  |  直到 14 年前
        1
  •  3
  •   beldaz Nicolas W.    14 年前

    STL返回引用的原因是因为模板化的代码不具备知道返回对象很小的奢侈。当一个 int 没问题,返回一个大型结构会毫无理由地减慢速度。在后一种情况下,使用引用是有意义的,因为一个合理的编译器可以优化使用基元类型的情况,您也可以在整个过程中使用引用。

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

    另外,将索引参数声明为 const 什么都不做,因为你通过了 index 你所做的一切就是阻止你自己改变 指数 在本地实施。如果你路过 指数 参考一下,那是另一回事,但你应该 be wary 这样做。

    最后,您真的确定您的类需要具有动态多态性(即,具有虚拟方法)吗?STL容器故意设计为不被继承(这就是为什么它们没有 virtual 析构函数)。容器并不是为了为派生类提供接口,而是为了便于实现。我认为,您建议的子类示例可以像模板化容器周围的包装类一样容易实现,支持通过组合而不是继承(这是 Gang of Four (除其他外)。除了良好的实践,避免 事实上的 方法保存对象中的vtables和相应指针,并要求在每个调用中进行额外的vtable查找。如果您真的不需要动态多态性,为什么要付出代价(并且可能会阻止编译器优化)?

        2
  •  2
  •   Mark Ransom    14 年前

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

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

    有一个参考的费用将很小,只是一个单一的取消参考。它甚至可能不是汇编级别的一个完整指令。