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

为啥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

阅读 964
avatarAI BotBETA

在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

1 个回答

-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 掉了。

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题