关于unique_ptr表演

cod*_*nk1 2 c++ gcc unique-ptr c++11

我经常读到unique_ptr在大多数情况下比shared_ptr更受欢迎,因为unique_ptr是不可复制的并且具有移动语义; shared_ptr会因复制和引用计数而增加开销;

但是当我在某些情况下测试unique_ptr时,它看起来比它的对手明显更慢(在访问中)

例如,在gcc 4.5下:

编辑:打印方法实际上不打印任何内容

#include <iostream>
#include <string>
#include <memory>
#include <chrono>
#include <vector>

class Print{

public:
void print(){}

};

void test()
{
 typedef vector<shared_ptr<Print>> sh_vec;
 typedef vector<unique_ptr<Print>> u_vec;

 sh_vec shvec;
 u_vec  uvec;

 //can't use initializer_list with unique_ptr
 for (int var = 0; var < 100; ++var) {

    shared_ptr<Print> p(new Print());
    shvec.push_back(p);

    unique_ptr<Print> p1(new Print());
    uvec.push_back(move(p1));

  }

 //-------------test shared_ptr-------------------------
 auto time_sh_1 = std::chrono::system_clock::now();

 for (auto var = 0; var < 1000; ++var) 
 {
   for(auto it = shvec.begin(), end = shvec.end(); it!= end; ++it)
   {
     (*it)->print();
   }
 }

 auto time_sh_2 = std::chrono::system_clock::now();

 cout <<"test shared_ptr : "<< (time_sh_2 - time_sh_1).count() << " microseconds." << endl;

 //-------------test unique_ptr-------------------------
 auto time_u_1 = std::chrono::system_clock::now();

 for (auto var = 0; var < 1000; ++var) 
 {
   for(auto it = uvec.begin(), end = uvec.end(); it!= end; ++it)
   {
     (*it)->print();
   }
 }

 auto time_u_2 = std::chrono::system_clock::now();

 cout <<"test unique_ptr : "<< (time_u_2 - time_u_1).count() << " microseconds." << endl;

}
Run Code Online (Sandbox Code Playgroud)

我平均得到(g ++ -O0):

  • shared_ptr:1480微秒
  • unique_ptr:3350微秒

差异来自哪里?这可以解释吗?

Soh*_*neh 19

更新于2014年1月1日

我知道这个问题很老了,但结果仍然适用于G ++ 4.7.0和libstdc ++ 4.7.所以,我试图找出原因.

什么你标杆这里是解引用性能使用-O0和,看着实行unique_ptrshared_ptr,你的结果实际上是正确的.

unique_ptr将指针和删除器存储在a中::std::tuple,同时shared_ptr直接存储裸指针句柄.因此,当您取消引用指针(使用*, - >或get)时,您需要额外调用::std::get<0>()in unique_ptr.相反,shared_ptr直接返回指针.在gcc-4.7上,即使在优化和内联时,:: std :: get <0>()比直接指针慢一点..优化和内联时,gcc-4.8.1完全省略了:: std :: get <0>()的开销.在我的机器上,当编译时-O3,编译器生成完全相同的汇编代码,这意味着它们在字面上是相同的.

总而言之,利用当前的实现,shared_ptr是在创建,移动,复制和引用计数速度较慢,但同样快速*上废弃*.

注意:问题中print()是空的,编译器在优化时会省略循环.所以,我稍微改变了代码以正确观察优化结果:

#include <iostream>
#include <string>
#include <memory>
#include <chrono>
#include <vector>

using namespace std;

class Print {
 public:
  void print() { i++; }

  int i{ 0 };
};

void test() {
  typedef vector<shared_ptr<Print>> sh_vec;
  typedef vector<unique_ptr<Print>> u_vec;

  sh_vec shvec;
  u_vec uvec;

  // can't use initializer_list with unique_ptr
  for (int var = 0; var < 100; ++var) {
    shvec.push_back(make_shared<Print>());
    uvec.emplace_back(new Print());
  }

  //-------------test shared_ptr-------------------------
  auto time_sh_1 = std::chrono::system_clock::now();

  for (auto var = 0; var < 1000; ++var) {
    for (auto it = shvec.begin(), end = shvec.end(); it != end; ++it) {
      (*it)->print();
    }
  }

  auto time_sh_2 = std::chrono::system_clock::now();

  cout << "test shared_ptr : " << (time_sh_2 - time_sh_1).count()
       << " microseconds." << endl;

  //-------------test unique_ptr-------------------------
  auto time_u_1 = std::chrono::system_clock::now();

  for (auto var = 0; var < 1000; ++var) {
    for (auto it = uvec.begin(), end = uvec.end(); it != end; ++it) {
      (*it)->print();
    }
  }

  auto time_u_2 = std::chrono::system_clock::now();

  cout << "test unique_ptr : " << (time_u_2 - time_u_1).count()
       << " microseconds." << endl;
}

int main() { test(); }
Run Code Online (Sandbox Code Playgroud)

注意:这不是一个基本问题,可以通过在当前libstdc ++实现中放弃使用:: std :: tuple来轻松修复.

  • 同样,使用`-O0`标志的性能基准是没有意义的. (5认同)
  • 我不相信你的:: std :: get <0>()参数.编译器掌握所有信息,以完全消除调用的所有其他内容,而不是实际的解除引用.无论它在实践中是否成功都可能是一个不同的问题...... (3认同)
  • 这是胡扯。通过启用编译器中的优化,可以轻松地修复它。图书馆没有理由改变行为。人们应该停止抱怨他们的未优化代码很慢。 (2认同)

Pup*_*ppy 12

您在定时块中所做的只是访问它们.这根本不会涉及任何额外的开销.增加的时间可能来自控制台输出滚动.你永远不可能在定时基准测试中做I/O.

如果你想测试引用计数的开销,那么实际上做一些引用计数.shared_ptr如果你永远不变异,shared_ptr那么建造,破坏,分配和其他变异操作的时间增加会如何影响你的时间?

编辑:如果没有I/O那么编译器优化在哪里?他们应该完成整个事情.甚至意识形态也让这个人大吃一惊.

  • 说实话,通过对测试的一些修改,我也看到了对unique_ptr值的**访问**一直慢了两倍(使用gcc 4.4.1 MinGW). - 据我所知,"你应该做一些重新计算"完全错过了所要求的内容.问题恰恰在于通过unique_ptr意外缓慢访问.欢迎推荐更好的性能分析技术,但不建议只计算不同的时间. (2认同)