在现代CPU中,虚拟成员函数对局部性是好是坏
virtual member functions are good or bad for locality in modern CPUs?
考虑到具有新的移动指令和新的内存控制器的新CPU,如果在C++中,我有一个Derived
对象的向量,其中Derived
由虚拟成员函数组成,这对局部性来说是好事还是坏事?
如果我有一个指向基类Base*
的指针向量,在那里我存储对从Base
向上1-2-3级别的派生对象的引用,该怎么办?
基本上,动态类型适用于这两种情况,但哪一种更适合缓存和内存访问?
我比较喜欢这两个,但我希望看到这个问题的完整答案。
在过去的2-3年里,硬件行业有什么新的东西可以考虑作为地面制动?
将Derived
而不是Base *
存储在向量中更好,因为它消除了一个额外的间接级别,并且您可以将所有对象"一起"布置在连续内存中,这反过来又使硬件预取器的生活更轻松,有助于分页、TLB未命中等。但是,如果您这样做,请确保不会引入切片问题。
至于这种情况下的虚拟调度,除了«this»指针所需的调整外,几乎无关紧要。例如,如果Derived
覆盖您正在调用的虚拟函数,并且您已经有指向Devied *
的指针,则不需要进行«this»调整,否则应将其调整为基类的«this»值之一(这也取决于继承层次结构中类的大小)。
只要向量中的所有类都有相同的重载,CPU就能够预测发生了什么。然而,如果你有不同的实现,那么CPU就不知道下一个对象将调用什么函数,这可能会导致性能问题。
别忘了在你做出改变之前和之后都要进行个人简介。
现代CPU知道如何优化依赖数据的跳转指令,以及如何优化依赖于数据的"分支"指令-处理器会"学习"上次我在这里,我是这样做的",如果它有足够的信心(经历了几次相同的结果),它会继续这样做。
当然,如果实例是对不同类的完全随机选择,并且每个类都有自己的虚拟函数,那么这也无济于事。
缓存局部性当然略有不同,它实际上取决于您是在向量中存储对象实例还是指向实例的指针/引用。
当然,一个重要的因素是"替代方案是什么?"——如果你"正确"地使用虚拟函数,这意味着在代码路径中(至少)少了一个条件检查,因为决策是在更早的阶段做出的。该条件将是(假设概率对应相同)决策的分支概率,如果你用其他方法来解决它——这对性能的影响至少和具有相同概率的virtual
函数一样糟糕(可能情况更糟,因为我们现在有一个if (x) foo(); else bar();
类型的场景,所以我们必须首先评估x
,然后选择路径。obj->vfunc()
将是不可预测的,因为获取vtable会产生不可预测结果,但至少vtable
本身是缓存的。
- "error: no matching function for call to"构造函数错误
- 什么时候调用组成单元对象的析构函数
- 继承函数的重载解析
- 为什么随机数生成器不在void函数中随机化数字,而在main函数中随机化
- C++模板来检查友元函数的存在
- 递归函数计算序列中的平方和(并输出过程)
- 对RValue对象调用的LValue ref限定成员函数
- C++17复制构造函数,在std::unordereded_map上进行深度复制
- 将数组作为参数传递给函数安全吗?作为第三方职能部门,可以探索他们想要的之外的其他元素
- 在C++STL中是否有Polyval(Matlab函数)等价物?
- 为什么使用 "this" 指针调用派生成员函数?
- 将对象数组的引用传递给函数
- 函数调用中参数的顺序重要吗
- 函数向量_指针有不同的原型,我可以构建一个吗
- 使用不带参数的函数访问结构元素
- 代码在main()中运行,但在函数中出现错误
- 内置函数可查看CPP中的成员变量
- 缓存局部性与函数调用
- 函数指针返回C++中另一个具有局部性的函数指针
- 在现代CPU中,虚拟成员函数对局部性是好是坏