通过shared_ptr传递的代价

我在整个应用程序中广泛使用std :: tr1 :: shared_ptr。 这包括作为函数parameter passing对象。 考虑以下:

class Dataset {...} void f( shared_ptr< Dataset const > pds ) {...} void g( shared_ptr< Dataset const > pds ) {...} ... 

虽然通过shared_ptr传递一个数据集对象保证了它在f和g中的存在,但这些函数可能被调用了数百万次,导致大量的shared_ptr对象被创build和销毁。 下面是最近运行的平面gprofconfiguration文件的一个片段:

每个样品计数为0.01秒。
  累计自我累计
 时间秒秒呼叫s /呼叫s /呼叫名称
   9.74 295.39 35.12 2451177304 0.00 0.00 std :: tr1 :: __ shared_count :: __ shared_count(std :: tr1 :: __ shared_count const&)
   8.03 324.34 28.95 2451252116 0.00 0.00 std :: tr1 :: __ shared_count ::〜__shared_count()

所以,~17%的运行时间用在了shared_ptr对象的引用计数上。 这是正常的吗?

我的应用程序的很大一部分是单线程的,我正在考虑重写一些函数

 void f( const Dataset& ds ) {...} 

并更换电话

 shared_ptr< Dataset > pds( new Dataset(...) ); f( pds ); 

 f( *pds ); 

在我知道的地方,当程序的stream程在f()内时,对象不会被破坏。 但在我改变一堆函数签名/调用之前,我想知道shared_ptr传递的典型性能是什么。 看起来像shared_ptr不应该用于经常被调用的函数。

任何input将不胜感激。 谢谢阅读。

-Artem

更新:在改变一些函数接受const Dataset& ,新的configuration文件看起来像这样:

每个样品计数为0.01秒。
  累计自我累计
 时间秒秒呼叫s /呼叫s /呼叫名称
   0.15 241.62 0.37 24981902 0.00 0.00 std :: tr1 :: __ shared_count ::〜__shared_count()
   0.12 241.91 0.30 28342376 0.00 0.00 std :: tr1 :: __ shared_count :: __ shared_count(std :: tr1 :: __ shared_count const&)

我对析构函数的调用次数比复制构造函数的调用次数有点困惑,但总的来说,我对于相关运行时间的减less感到非常满意。 感谢所有的build议。

总是通过const引用传递你的shared_ptr

 void f(const shared_ptr<Dataset const>& pds) {...} void g(const shared_ptr<Dataset const>& pds) {...} 

编辑:关于他人提到的安全问题:

  • 在整个应用程序中大量使用shared_ptr ,按值传递将占用大量时间(我已经看到了这个值超过了50 +%)。
  • 使用const T&而不是const shared_ptr<T const>&当参数不能为空时。
  • 当性能问题时,使用const shared_ptr<T const>&const T*更安全。

您只需要shared_ptr将其传递给保留以备将来使用的函数/对象。 例如,某些类可能会保留shared_ptr以便在工作线程中使用。 对于简单的同步调用,使用普通指针或引用就足够了。 shared_ptr不应该使用纯指针完全replace。

如果你不使用make_shared ,你可以放弃吗? 通过将引用计数和对象定位在内存的相同区域,您可能会看到与高速caching一致性相关的性能增益。 无论如何值得一试。

在性能关键的应用程序中,应避免任何对象创build和销毁,尤其是冗余对象创build和销毁。

考虑一下shared_ptr在做什么。 它不仅创build了一个新的对象并填充它,而且还引用共享状态来增加引用信息,并且对象本身可能完全存在于其他地方,这对您的caching将是一个噩梦。

据推测,你需要shared_ptr(因为如果你能摆脱一个本地对象,你不会从堆中分配一个),但你甚至可以“caching”shared_ptr解引用的结果:

 void fn(shared_ptr< Dataset > pds) { Dataset& ds = *pds; for (i = 0; i < 1000; ++i) { f(ds); g(ds); } } 

…因为即使* pds需要比绝对必要的更多的记忆。

这听起来像你真的知道你在做什么。 您已经对应用程序进行了描述,并且确切知道正在使用的周期。 你明白,调用构造函数的引用计数指针是昂贵的,只要你不断地做。

唯一可以给你的是:假设在函数f(t * ptr)内部,如果调用另一个使用共享指针的函数,并且其他函数(ptr)和其他函数使得共享指针指向原始指针。 当第二个共享指针的引用计数达到0,那么你已经有效地删除了你的对象….即使你不想。 你说过你使用了很多引用计数指针,所以你必须小心这样的angular落案例。

编辑:你可以使析构函数私有,只有共享指针类的朋友,这样的析构函数只能通过共享指针调用,那么你是安全的。 不防止共享指针的多个删除。 根据Mat的评论。