std::shared_ptr拷贝与移动的开销对比及优化必要性问询
底层性能差异的核心原因
std::shared_ptr的内部实现包含两个指针:一个指向托管的对象,另一个指向包含强引用计数、弱引用计数、删除器的控制块。- 拷贝
std::shared_ptr时,必须执行线程安全的原子加操作修改控制块中的强引用计数,确保多线程场景下引用计数的正确性。 - 移动
std::shared_ptr时,仅需要将原对象持有的两个指针赋值给新对象,再将原对象的两个指针置空即可,全程不需要触碰控制块,也没有任何原子操作,所有权直接转移,引用计数完全不变。
实际开销差距
在主流x86-64架构下:
- 移动
std::shared_ptr的开销等价于两次64位指针的拷贝操作,仅需3~5个CPU时钟周期,且编译器可以根据上下文对移动操作做进一步优化,甚至在返回值优化等场景下完全消除移动开销。 - 无竞争场景下拷贝
std::shared_ptr的原子加操作需要20~80个CPU时钟周期,是移动开销的几倍到几十倍。如果是多线程同时拷贝同一个std::shared_ptr,控制块对应的缓存行会在多个CPU核心之间频繁同步失效,拷贝开销会飙升到几百甚至上千个时钟周期,差距会拉到上百倍。
高频场景下的优化价值
这种优化在高频率使用std::shared_ptr的场景下收益非常明显,典型适用场景包括:
- 高性能消息队列中用
std::shared_ptr托管大数据包,每秒传递次数达到十万甚至百万级别时,用移动代替拷贝可以降低几十倍的相关耗时。 - 容器存储大量
std::shared_ptr时,做排序、插入、删除、扩容等操作,使用移动语义可以完全省略海量的原子引用计数操作,整体操作速度可以提升数倍到数十倍。 - 函数传参时如果不需要保留原
std::shared_ptr的所有权,直接用std::move转移所有权,比拷贝传参的收益会随着调用频率升高而线性增长。
只有在单次程序生命周期内std::shared_ptr的拷贝次数不超过几千次的低频场景下,这种优化的收益才会感知不明显。
内容的提问来源于stack exchange,提问作者ManuelSchneid3r
相关产品推荐
相关产品推荐

