2

考虑到带有用于移动和新内存控制器的新指令的新 CPU,如果在 C++ 中我有一个由虚拟成员函数组成的Derived对象向量Derived,这对本地来说是好事还是坏事?

如果我有一个指向基类的指针向量,在该基类Base*中存储对从 1-2-3 级的派生对象的引用,该Base怎么办?

基本上动态类型适用于这两种情况,但哪一种更适合缓存和内存访问?

我在这两个之间有偏好,但我希望看到关于这个主题的完整答案。

在过去的 2-3 年里,硬件行业有什么新的东西需要考虑作为突破?

4

2 回答 2

1

存储Derived而不是存储Base *在向量中更好,因为它消除了一个额外的间接级别,并且您将所有对象“一起”布置在连续内存中,这反过来使硬件预取器的生活更轻松,有助于分页,TLB未命中等. 但是,如果您这样做,请确保您不会引入切片问题。

至于这种情况下的虚拟调度,除了«this»指针需要调整外,几乎没有关系。例如,如果Derived覆盖了您正在调用的虚函数并且您已经有一个指向 的指针Devied *,则不需要调整 «this»,否则应将其调整为基类的 «this» 值之一(这也是取决于继承层次结构中类的大小)。

只要向量中的所有类都具有相同的重载,CPU 就能够预测正在发生的事情。但是,如果您混合了不同的实现,那么 CPU 将不知道将为每个下一个对象调用什么函数,这可能会导致性能问题。

并且不要忘记在进行更改之前和之后始终进行概要分析。

于 2013-08-07T16:40:46.927 回答
1

现代 CPU 知道如何优化数据相关的跳转指令,以及它可以优化数据相关的“分支”指令——处理器将“学习”“上次我经过这里,我走了这条路”,如果它有足够的信心(经历了几次相同的结果)它会一直这样下去。

当然,如果实例是完全随机选择的不同类,每个类都有自己的虚函数,那也无济于事。

缓存局部性当然是一个稍微不同的问题,它实际上取决于您是存储对象实例还是向量中实例的指针/引用。

当然,一个重要的因素是“有什么选择?” - 如果您“正确”地使用虚函数,则意味着在代码路径中(至少)少了一个条件检查,因为该决定是在更早的阶段做出的。如果您通过其他方法解决它,那么该条件将是(假设概率对应相同)决策的分支概率 - 这至少virtual与具有相同概率的函数一样糟糕(可能会更糟) ,因为我们现在有一个if (x) foo(); else bar();类型场景,所以我们首先必须评估x然后选择路径。obj->vfunc()这将是不可预测的,因为获取 vtable 会产生不可预测的结果 - 但至少它vtable本身被缓存了。

于 2013-08-07T16:44:44.857 回答