C++运行时中虚分派比函数模板更快的场景有哪些?
代码缓存命中率更高的场景
模板会给每个不同类型生成独立的实例化代码,很容易导致二进制体积膨胀(也就是常说的“代码爆炸”)。当实例化的类型多到一定程度,CPU的指令缓存(ICache)就装不下这么多重复逻辑的代码,命中率暴跌。而虚分派的代码是共享的,所有派生类复用同一套虚函数实现,能大幅减少冗余代码,提升ICache命中率。在循环密集、频繁调用这类函数的场景里,缓存带来的速度提升会完全盖过虚表查找的那点开销。实际运行类型远少于模板支持类型的场景
比如有些系统设计上支持几十上百种类型,但实际运行时只会用到其中两三种。这时候模板提前为所有潜在类型生成的代码全是冗余的,不仅占空间,还拖慢缓存。而虚分派只需要维护一套共享代码,虚表查找的单次开销(本质就是一次指针间接访问),和缓存失效带来的性能损失比起来根本不值一提。像很多插件系统就属于这种情况,虽然理论上兼容N种插件,但实际运行时只加载少数几个。模板优化受限而虚函数更易被编译器优化的场景
有些复杂的模板代码,因为过度特化、嵌套层次深或者类型推导逻辑复杂,编译器没法做有效的内联、循环展开这类优化。反而虚函数的逻辑通常更单一,编译器能对共享的虚函数实现做深度优化。另外,如果模板函数依赖外部复杂的运行时状态,编译期优化本来就施展不开,这时候虚函数的调用路径更清晰,反而能跑出更快的速度。虚函数调用具有强规律性的场景
如果程序运行时很长一段时间里,只会调用某一个派生类的虚函数,CPU的分支预测器会记住这个访问模式,把虚分派的间接调用开销降到几乎为0。而模板生成的多个函数实例,会让CPU面对更多不同的调用目标,分支预测更容易失败,导致流水线停顿,反而拖慢速度。
内容的提问来源于stack exchange,提问作者bedman3

