You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C++17 std::variant实现为何比动态多态性能慢1.3-1.5倍?

性能差异原因分析
  • 调度实现开销不同
    虚函数调用的逻辑非常简洁:从对象指针头部读取虚表指针,按固定偏移取对应函数地址直接跳转调用,只有一次内存访问开销。在你的测试场景中,每列对应的比较函数类型固定,CPU分支预测对这种稳定的间接跳转命中率接近100%,几乎没有额外损耗。
    std::visit在GCC 10中的实现是先读取std::variant的类型标签,做边界校验后再查跳转表调用对应逻辑,比虚函数多了类型标签读取、边界判断两步操作。你的比较逻辑本身只有一次内存访问+相等判断,属于极轻量操作,多出来的调度开销占比被直接放大,最终表现就是整体速度更慢。
  • 内存布局没有优势
    你的动态多态实现用std::vector<Comp*>存储比较器,每个子类对象的内存布局是虚表指针+两个数据指针,固定大小的内存布局对CPU缓存非常友好。而std::variant<EqualToI, EqualToF>的内存布局是两个数据指针+类型标签,对齐后和虚函数子类大小几乎一致,并没有缓存局部性的优势,抵消了variant原本可能的性能收益。
  • 编译器版本优化不足
    GCC 10对C++17 std::visit的优化还不完善,后续GCC 12及以上版本对variant调度的优化做了大量改进,同测试场景下两者的性能差距会缩小到10%以内,部分场景甚至variant会反超。
基准测试合理性说明

你的实现和测试逻辑没有问题。通常宣传std::variant性能优于虚函数的场景,都是可以在编译期确定variant类型、让编译器直接把visit展开成直接函数调用的场景,这种场景下确实可以完全消除运行期调度开销,比虚函数更快。但你的使用场景是运行期动态生成异构比较器列表,编译器无法做编译期展开,只能走运行期跳转逻辑,这种场景下虚函数的调度效率本身就更高,出现性能差是正常现象。

如果想要进一步优化性能,可以尝试把列类型的顺序和数量在编译期固定,用std::tuple存储比较器,遍历tuple的时候可以完全消除所有运行期调度,性能会比现在两个方案都高1倍以上。

内容的提问来源于stack exchange,提问作者n1r44

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.01 02:27:04