shared_ptr的线程安全开销是多少?

tjh*_*nce 7 c++ multithreading shared-ptr

std::shared_ptr保证是线程安全的.我不知道典型实现使用什么机制来确保这一点,但肯定它必须有一些开销.即使您的应用程序是单线程的,也会出现这种开销.

以上情况如何?如果是这样,那是否意味着它违反了"你不为不使用的东西付费"的原则,如果你没有使用线程安全保证?

Sha*_*our 9

如果我们查看std :: shared_ptr的cppreference页面,他们在Implementation notes部分中声明了以下内容:

为了满足线程安全要求,引用计数器通常使用带有std :: memory_order_relaxed的std :: atomic :: fetch_add递增和递减.

有趣的是要注意实际的实现,例如libstdc ++实现文档在这里说:

对于libstdc ++中的shared_ptr版本,编译器和库是固定的,这使得事情变得更加简单:我们有一个原子CAS或者我们没有,请参阅下面的锁定策略以获取详细信息.

选择锁定策略部分说(重点煤矿):

有一个_Sp_counted_base类,它是在枚举__gnu_cxx :: _ Lock_policy上参数化的模板.整个类系列在锁定策略上进行参数化,直到__shared_ptr,__ weak_ptr和__enable_shared_from_this.实际的std :: shared_ptr类继承自__shared_ptr,并根据libstdc ++配置的线程模型和平台自动选择锁定策略参数,以便使用最佳可用模板特化.这种设计是必要的,因为即使它具有默认值,shared_ptr也不符合额外的模板参数.可用的政策是:

[...]

3._S_Single

此策略使用不可重入的add_ref_lock()而不进行锁定.在没有--enable-threads的情况下构建libstdc ++时使用它.

并进一步说(强调我的):

对于所有三个策略,引用计数增量和减量是通过ext/atomicity.h中的函数完成的,它们检测程序是否是多线程的.如果程序中只存在一个执行线程,则使用较便宜的非原子操作.

因此,至少在此实现中,您不需要为不使用的内容付费.


Mic*_*son 4

至少在 i386 上的 boost 代码中,boost::shared_ptr是使用原子 CAS 操作实现的。这意味着虽然它有一些开销,但它相当低。我希望 的任何实现都是std::shared_ptr类似的。

在高性能数字代码的紧密循环中,我发现通过切换到原始指针并非常小心,可以提高一些速度。但对于普通代码 - 我不会担心它。

  • 原子操作非常昂贵。上次我测量时,我相信在典型的 Intel CPU 上每个原子操作通常大约需要 20 ns。比常规操作慢大约 100 倍。 (3认同)