当前位置: 首页 > 知识库问答 >
问题:

c++ - 为啥unique_ptr的移动比shared_ptr赋值要慢?

孟豪
2024-02-01

为啥unique_ptr的移动比shared_ptr赋值要慢?
在Modern Effective C++中,提倡使用unique_ptr代替裸指针,因为unique_ptr的大小和性能与裸指针基本一致但更安全,而shared_ptr由于由原子变量的存在性能更差,但是同步赋值试下来,unique_ptr的移动很慢。

#include <memory>#include <chrono>#include <utility>#include <iostream>int main(){    #define COUNT 100000000    using MyType = int;    decltype(auto) u = std::make_unique<MyType>();    decltype(auto) s = std::make_shared<MyType>();    decltype(auto) r = new MyType;    decltype(auto) u1 = std::move(u);    decltype(auto) s1 = s;    decltype(auto) r1 = r;    auto start = std::chrono::high_resolution_clock::now();    auto finish = std::chrono::high_resolution_clock::now();    std::chrono::duration<double> elapsed = finish - start;    start = std::chrono::high_resolution_clock::now();    for (int i = 0; i < COUNT; ++i)  r = r1;    finish = std::chrono::high_resolution_clock::now();    elapsed = finish - start;    std::cout << "ptr: " << elapsed.count() << "s\n";    start = std::chrono::high_resolution_clock::now();    for (int i = 0; i < COUNT; ++i) u = std::move(u1);    finish = std::chrono::high_resolution_clock::now();    elapsed = finish - start;    std::cout << "unique_ptr: " << elapsed.count() << "s\n";    start = std::chrono::high_resolution_clock::now();    for (int i = 0; i < COUNT; ++i) s = s1;    finish = std::chrono::high_resolution_clock::now();    elapsed = finish - start;    std::cout << "shared_ptr: " << elapsed.count() << "s\n";    }

运行结果:
image.png

共有2个答案

訾凯歌
2024-02-01

-O0 肯定是不行的,C++ 比速度一定要开优化。

不开优化的话,这里的 std::move 怎么说也是一个函数调用。多了一个函数调用就没法比了。

.L51:        mov     rax, QWORD PTR [rbp-48]        mov     rdi, rax        call    std::remove_reference<std::unique_ptr<int, std::default_delete<int> >&>::type&& std::move<std::unique_ptr<int, std::default_delete<int> >&>(std::unique_ptr<int, std::default_delete<int> >&)        mov     rdx, rax        lea     rax, [rbp-120]        mov     rsi, rdx        mov     rdi, rax        call    std::unique_ptr<int, std::default_delete<int> >::operator=(std::unique_ptr<int, std::default_delete<int> >&&)        add     DWORD PTR [rbp-24], 1.L50:        cmp     DWORD PTR [rbp-24], 999999        jle     .L51

另外,你这个 unique_ptr 的测试里还多了一个 delete ,因为在第二轮循环 u1 为 nullptr ,u 就被 delete 掉了。

陈奇希
2024-02-01

在C++中,unique_ptrshared_ptr是两种常用的智能指针类型,它们在内存管理方面的工作方式有所不同,这导致了它们在性能上的差异。

unique_ptr代表独占所有权的智能指针。当一个unique_ptr对象被移动时,所有权会发生转移,原始unique_ptr将变为空。这个所有权转移是通过std::move来完成的,它实际上是一个非常轻量级的操作,只涉及到设置一个内部指针。因此,unique_ptr的移动速度是非常快的。

相比之下,shared_ptr则代表共享所有权的智能指针。当一个shared_ptr对象被赋值时,它会增加引用计数。如果这个引用计数增加了,那么就会为共享的原始对象分配更多的内存来存储这个计数。由于涉及到内存分配和原子操作,因此shared_ptr的赋值操作比unique_ptr的移动操作要慢得多。

在你的代码中,你测试了三种类型的指针:裸指针、unique_ptrshared_ptr。对于裸指针,你只是进行了赋值操作,而没有涉及到内存分配或释放。因此,这个操作是最快的。对于unique_ptr,你使用了移动语义来进行赋值,这是一个非常快速的操作。而对于shared_ptr,你进行了赋值操作,这涉及到了引用计数的增加,因此速度较慢。

总的来说,由于unique_ptrshared_ptr在实现上存在本质的不同,导致了它们在性能上的差异。在实际编程中,我们应根据具体需求选择使用哪种类型的智能指针。如果只需要独占所有权,那么使用unique_ptr是更好的选择;如果需要共享所有权,那么可以使用shared_ptr

 类似资料:
  • 我正在使用STD::UNIQUE_PTR在类上创建一些公共成员,这些成员必须是不可复制或可移动的。但是STD::UNIQUE_PTR是可移动的,我想知道如果有人移动STD::UNIQUE_PTR包含的指针,然后我尝试访问被移动的那个类的STD::UNIQUE_PTR成员,会发生什么。 我所需要的就是使std::unique_ptr不可移动,并简单地保存一个唯一的指针,该指针不能从拥有它的类中移动或

  • 我有一个类实例的向量。这些实例中的每一个都有一个指向另一个类的unique_ptr。由于我从来不尝试复制类实例,甚至不尝试共享指针,所以我觉得unique_ptr比shared_ptrs更合适,因为指针不是共享的,而是只能通过类实例访问。 这是不好的练习吗?为什么这行不通?我知道将一个实例复制到一个唯一的指针将是错误的,但是既然我移动了它,我不明白为什么不允许这样做? 我必须创建一个自定义移动构造

  • 代码如下: 我假设错误发生在insert函数中,并且与参数初始化有关。 bintree.cpp:65:27:错误:使用删除的函数'std::unique_ptr<_tp,_dp>::unique_ptr(const std::unique_ptr<_tp,_dp>&)[with_tp=bintreenode;_dp=std::default_delete>]“tree.insert(tree.ro

  • 本文向大家介绍Vue.js 动态为img的src赋值方法,包括了Vue.js 动态为img的src赋值方法的使用技巧和注意事项,需要的朋友参考一下 需求是这样: ajax获取数据如下 然后渲染列表到页面,如果男,则将img的src设为"images/male.png",反之设为"images/female.png" 两个都可以实现,为了在html中看起来舒服点还是用filter吧,虽然也就一个判断

  • 本文向大家介绍在C / C ++中为浮点数和比较赋一个整数,包括了在C / C ++中为浮点数和比较赋一个整数的使用技巧和注意事项,需要的朋友参考一下 整数是一种数据类型,用于定义一个包含所有正,负或零个非小数值的数字。这些不能有小数。 浮点数是一种数据类型,用于定义具有小数值的数字。这些也可以有小数。 现在,当我们为两者输入相同的值时,我们将检查编译器返回的float和integer值是什么。