虚拟方法开销是否显著
Is virtual methods overhead significant?
我有一些复杂的继承结构,主要是为了避免代码重复和方便各种类的通用接口。它依赖于虚拟和非虚拟继承,看起来或多或少是这样的:
class AbstractItem
{
//bunch of abstract methods
};
class AbstractNode : virtual public AbstractItem
{
//some more virtual abstract methods
};
class AbstractEdge : virtual public AbstractItem
{
//yet some different virtual abstract methods
};
然后是一些像这样的"真实"类
class Item : virtual public AbstractItem
{
//implements AbstractItem
};
class Node : public Item, public AbstractNode
{
//implements AbstractNode
};
class Edge : public Item, public AbstractEdge
{
//implemetns AbstractEdge
};
并将其打包到一个图形模型类中,以便:
class AbstractGraph
{
virtual QList<AbstractNode*> nodes() const = 0;
virtual QList<AbstractEdge*> edges() const = 0;
};
class GraphModel : public AbstractGraph
{
public:
virtual QList<AbstractNode*> nodes() const override; //this converts m_Nodes to a list of AbstractNode*
virtual QList<AbstractEdge*> edges() const override; //dtto
private:
QList<Node*> m_Nodes;
QList<Edge*> m_Edge;
};
这种复杂结构的原因是,有不同的类实现AbstractGraph
,如排序模型、过滤,这些类有不同的变体-有些类按照所示的模型存储数据,并有自己的AbstractItem/Node/Edge派生类集,另一些类是动态的,依赖于底层图/模型的数据,而没有自己的数据。示例:
class FilterNode : public AbstractNode
{
//access the data in the m_Item via AbstractItem interface and implements differently AbstractNode interface
private:
AbstractItem *m_Item = nullptr; //this variable holds pointer to some real item with actual data such as the one from GraphModel
};
class GraphFilter : public AbstractGraph
{
//implements the interface differently to the GraphModel
private:
QList<FilterNode*> m_Nodes;
AbstractGraph *m_Source = nullptr; //source graph...
};
我对此有了第二个想法,因为它依赖于(虚拟)继承,依赖于通过base等调用的抽象方法。由此产生的开销有那么大吗
另一种选择是:
a) 复制粘贴大量代码以避免虚拟方法和大部分继承,但这将是代码维护的噩梦。再加上没有通用接口。。。
b) 以某种方式将其全部模板化。。。我有点不确定,甚至不知道这是否可能。为了避免代码重复,我已经在其中的一些地方使用了它们。
那么,这看起来合理吗?还是有些过头了?我可能会补充说,在某些情况下,我会绕过虚拟调用直接调用方法(在模型内部),但在外部,它几乎总是通过抽象库进行调用。
试图用C++实现使用动态多态性的通用图算法会使事情变得
- 不必要的硬
- 不必要的慢
函数越简单,虚拟函数开销就越显著。在引用的接口中,您还可以从各种函数返回容器。即使这些是COW容器,也涉及一些工作,随意访问序列可能很容易取消共享(即复制)表示。
在遥远的过去(大约1990年至1996年),我曾尝试过一种基于动态多态性的图算法的通用实现,并一直在努力解决各种问题以使其发挥作用。当我第一次读到STL时,发现大多数问题都可以通过类似的抽象来解决(尽管仍然缺少一个关键思想:属性映射;有关详细信息,请参阅下面对BGL的引用)。
我发现用类似STL的抽象来实现图算法更可取。算法是根据特定的概念实现的函数模板,这些概念有点像基类,除了两个关键区别:
- 抽象中不涉及虚拟函数调用,函数通常可以内联
- 从函数返回的类型只需要建模一个合适的概念,而不必通过某种形式的继承来兼容某个特定的接口
无可否认,我是有偏见的,因为我的毕业论文就是关于这个主题的。对于这种方法的[独立开发]应用程序,请查看Boost图库(BGL)。
对于比较不同函数调用方法的一些性能度量,请查看函数调用基准。它们是根据性能TR中函数调用的性能测量进行建模的。
- C++标准是否允许<double>在没有开销的情况下实现 std::可选
- 使用静态成员函数而不是普通函数是否有任何开销?
- 在 v8 JavaScript 中重复调用C++是否有巨大的开销?
- 与纯 V8 相比,NodeJS 是否有任何性能缺陷或显著开销?
- 非 constexpr 变量模板的开销是否为零?
- 右值引用是否具有与右值引用相同的开销?
- 这种获取模板参数包中最后一个元素的方法是否有隐藏的开销?
- char 和 char& 之间是否存在相对复制开销差异?
- 在方法中调用方法是否会导致开销
- 使用递归模板函数是否会引入函数调用开销,或者编译器是否大部分时间都内联它(下面的示例)?
- 虚拟继承是否会增加开销
- Getters和Setters.是否存在性能开销
- std::static_pointer_cast是否有任何额外的运行时开销
- 调用链函数是否会产生开销
- C++:如果所有数据可能都已有效,则在构造函数中进行验证是否有开销
- parallel_fo(Inter-TBB)上是否存在与我们在std::function上看到的开销类似的开销
- 虚拟方法开销是否显著
- C++智能指针取消引用 - 检查它是否已初始化的开销是多少?
- 引用静态函数作为类定义的一部分是否有任何开销?
- 在vector中存储boost_shared指针——是否开销大?