基准测试中移动std::vector比拷贝耗时更高?测试方法是否存在问题
问题分析与解答
一、性能测试的设计缺陷
你的测试结果反常的核心原因是测试代码存在多处设计漏洞,没有正确隔离变量、排除编译器和系统层面的干扰:
- 测试命名与实际操作不符:第二个测试的
src是左值,你测的是左值拷贝,不是标注的「rvalue拷贝」;第三个测试在C++17及以上标准中属于*强制复制消除(Guaranteed Copy Elision)*场景,std::vector<float> dst = 临时对象不会调用移动构造,直接在dst的内存地址上构造对象,你测的根本不是移动操作的耗时。 - 堆分配预热与顺序干扰:第一个循环已经做了1000万次700float大小的内存分配、释放操作,系统堆分配器已经把对应大小的内存块做了缓存,第二个拷贝测试的内存分配可以直接从缓存取,耗时自然偏低;而到第三个测试时,可能出现分配器缓存耗尽、内存碎片、CPU因为散热降频等问题,反而耗时更高,这是测试顺序导致的误差,和移动操作本身的性能无关。
- 无用代码消除风险:你循环内构造的
dst没有被实际使用,编译器在高优化等级下可能直接把整个循环优化掉,导致计时完全失真。
二、两个反常现象的具体原因
1. 移动比拷贝耗时更高
如前面所说,这是测试顺序带来的系统干扰导致的:
- 前两个测试已经消耗了大量堆缓存、拉高了CPU温度导致睿频下降,第三个测试天然处于不利的运行环境
- 你测的根本不是移动操作,而是临时对象直接构造的耗时,和移动语义无关
你可以把三个测试的顺序调换重新运行,会发现最先跑的测试耗时最高,后面的测试耗时更低,完全和操作本身的性能无关。
2. 注释第一个循环后耗时变化
注释第一个循环后:
- 第一个计时区间没有可执行代码,所以输出0ms是完全正常的
- 没有了前面1000万次内存操作的预热干扰,拷贝和复制消除的直接构造耗时才回归正常:直接构造不需要拷贝700个float的数据,只需要一次内存分配,自然比拷贝操作更快,符合预期。
三、移动语义的实现规则
编译器自动处理的场景
不需要手动实现移动语义,编译器生成的默认移动操作性能最优:
- 你的类没有自定义拷贝构造函数、拷贝赋值运算符、析构函数
- 类的所有非静态成员都支持移动操作
- 不需要自定义移动后的对象状态
需要手动实现移动语义的场景
只有以下情况需要手动编写移动构造/移动赋值运算符:
- 类自行管理原生堆资源(比如持有raw pointer、文件句柄等非RAII资源),默认移动操作无法正确转移资源所有权
- 需要给移动操作添加
noexcept修饰(标准库容器扩容时会优先调用noexcept的移动构造,否则会回退到拷贝) - 需要自定义移动后的原对象状态,或者类成员存在不支持默认移动的特殊类型
内容的提问来源于stack exchange,提问作者joaocandre
相关产品推荐
相关产品推荐

