【问题标题】:Performance impact of virtual inheritance虚拟继承的性能影响
【发布时间】:2011-06-21 10:37:39
【问题描述】:

我正在考虑在实时应用程序中使用虚拟继承。使用虚拟继承是否会产生类似于调用虚拟函数的性能影响?有问题的对象只会在启动时创建,但我担心层次结构中的所有函数都将通过 vtable 调度,或者只有来自虚拟基类的函数才会调度。

【问题讨论】:

  • 除非您使用多重继承,否则实际上不需要使用虚拟继承。
  • @ZacHowland,除非您在 Google 测试中使用模拟。 (gtest)
  • @Amigable:没有“除非”。使用 Google Test 时,在处理多重继承时,您仍然只使用虚拟继承。
  • @ZacHowland,在这种情况下我的错误。我认为能够模拟课程是必要的。但我还没有真正开始使用它......所以我知道什么。
  • Google 测试框架允许您为非纯抽象类创建模拟对象,因此当您从这些类继承时,您可能会遇到需要虚拟继承的菱形情况(多重继承) .但是,使用纯抽象(又名“接口”)并使用新实现模拟接口仍然是一种更好的做法。它将使您的测试代码更易于编写和维护(除了使您的实际代码更易于编写和维护)。

标签: c++ performance real-time virtual-inheritance


【解决方案1】:

通用实现将使对虚拟基类的数据成员的访问使用额外的间接方式。

正如 James 在他的 cmets 中指出的那样,在多继承场景中调用基类的成员函数将需要调整 this 指针,如果该基类是虚拟的,则基类的偏移量 sub -object 派生类对象中的对象取决于派生类的动态类型,需要在运行时计算。

这是否对实际应用程序有任何明显的性能影响取决于很多事情:

  • 虚拟基地有数据成员吗?通常,需要从虚拟派生抽象基类,而具有任何数据成员的抽象基类通常都是代码异味。

  • 假设您有带有数据成员的虚拟基地,那么这些虚拟基地是否在关键路径中?如果用户单击 GUI 中的某个按钮会导致几十个额外的间接访问,那么没人会注意到。

  • 如果避免使用虚拟碱基,替代方案会是什么?不仅设计可能较差,替代设计也可能对性能产生影响。毕竟,它必须实现和 TANSTAAFL 相同的目标。然后你用一种性能损失换取另一种加上劣质的设计。


补充说明:看看 Stan Lippmann 的 Inside the C++ Object Model,它非常彻底地回答了这些问题。

【讨论】:

  • 澄清一下,只有调用虚拟基类成员/函数会导致额外的间接性?
  • @Graeme:我当然不是这方面的专家,但我不明白对虚拟基类成员 functions 的调用会如何导致性能损失。它们要么静态分派(B::f()),要么通过派生类的虚拟表动态分派,就像非虚拟基的成员函数一样。 ICBWT。
  • @sbi:如果函数不是虚函数,那么可以静态选择要调用的函数,但是this指针必须在运行时计算[或查找],对吧?跨度>
  • @James:哦,对了,我忘了this指针调整。但这是由于多重继承,无论是否是虚拟的,还是我忘记了一些额外的成本?
  • @sbi:我认为这是计算和查找之间的区别。在非虚拟多重继承中,您可以将已知偏移量添加到您必须获得this 指针的任何指针。在虚拟多重继承中,您必须(在运行时)查找基类子对象所在的位置以获取this 指针。查找比添加“更昂贵”,但是这种差异的相关性取决于应用程序:正如您所说;除非这是具有实时性能限制的程序中的时间关键操作,否则这无关紧要。
【解决方案2】:

看看下面发表的 OOPSLA'96 的大规模实验研究。我正在复制粘贴 bibtex 条目、摘要和论文链接。我认为这是迄今为止关于该主题的最全面的实验研究。

@article{driesen1996direct,
  title={{The direct cost of virtual function calls in C++}},
  author={Driesen, K. and H{\\"o}lzle, U.},
  journal={ACM Sigplan Notices},
  volume={31},
  number={10},
  pages={306--323},
  issn={0362-1340},
  year={1996},
  publisher={ACM}
}

摘要: 我们研究虚函数的直接成本 假设标准,在 C++ 程序中调用 使用虚函数表实现。我们 实验性地测量这个开销对于一些 大型基准程序,使用组合 可执行检查和处理器模拟。我们的 结果表明,测量的 C++ 程序花费了 5.2% 的时间和 3.7% 的时间 调度代码中的指令。对于“所有虚拟” 程序的版本,中位开销上升到 13.7%(指令的 13%)。 “thunk”变体 虚函数表实现的减少 相对于 标准执行。在未来的处理器上,这些 间接费用可能会适度增加

http://www.cs.ucsb.edu/~urs/oocsb/papers/oopsla96.pdf

【讨论】:

  • 如果编译器优化使这项研究过时,我不会感到惊讶。它于 1996 年出版。
  • 嗯。但问题是关于虚拟继承,而不是关于虚拟函数
  • 这项研究是在 1995 年的处理器上完成的。只研究了 32 位处理器(具有 256K 缓存(8K 指令缓存))。当前的桌面处理器是 64 位的,具有 8MB 缓存。所研究的 Intel Pentium Pro 没有 MMX。不妨将 Windows 95 与 Windows 10 进行比较(修辞评论;请不要)。
【解决方案3】:

你确定你的意思是虚拟继承吗?如果是这样,它与普通虚函数调用的成本相同。 vtable 链式搜索只是遵循指定的路径。

你说这是在启动时。您的磁盘开销(从简单地将代码加载到内存中)可能需要比六条指令多几个数量级的时间来进行 vtable 查找。如果您能对此进行分析并发现差异,我会感到有些惊讶。

【讨论】:

    【解决方案4】:

    在不检查编译或运行时细节的情况下,根据我使用 GNU C++17 的测试,访问虚拟基类中的数据成员不会影响性能。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-04-29
      • 1970-01-01
      • 2016-10-26
      • 2013-08-24
      • 1970-01-01
      • 2019-06-19
      相关资源
      最近更新 更多