开门见山好了,boost 1.33 对于 boost 1.32 的 shared_ptr 和 weak_ptr 有一个不小的改变,然而这个改变如此透明,以至于它甚至于没有出现在 boost 1.33 的 release notes 中。
在 1.32 中,shared_ptr 和 weak_ptr 的引用计数使用锁来保证线程安全,下面一段摘自 boost 1.32 的 sp_counted_base 实现,大家可以在 boost 的 detail/shared_count.hpp 中找到它:
void add_ref_copy() {#if defined(BOOST_HAS_THREADS) mutex_type::scoped_lock lock(mtx_);#endif ++use_count_; } void add_ref_lock() {#if defined(BOOST_HAS_THREADS) mutex_type::scoped_lock lock(mtx_);#endif if(use_count_ == 0) boost::throw_exception(boost::bad_weak_ptr()); ++use_count_; }其中 add_ref_copy 是用于shared_count ,后者是用于 shared_ptr 的引用计数器;add_ref_lock 用于 weak_count ,后者是用于 weak_ptr 的引用计数器。
可以看到,在多线程环境中,boost 1.32 用 scoped_ptr 来保证引用计数器访问的串行性。但是在 boost 1.33 中,情况却大大不同了,它使用的是 lock free 的算法,在 Win32 平台下面就是通过操作系统的 InterlockedIncrement / InterlockedDecrement 以及 InterlockedCompareExchange 来完成。下面的代码来自 boost 1.33 的 sp_counted_base 实现,位于 boost 的 detail/sp_counted_base_w32.hpp 文件中:
void add_ref_copy() { BOOST_INTERLOCKED_INCREMENT( &use_count_ ); } bool add_ref_lock() // true on success { for( ;; ) { long tmp = static_cast< long const volatile& >( use_count_ ); if( tmp == 0 ) return false; if( BOOST_INTERLOCKED_COMPARE_EXCHANGE( &use_count_, tmp + 1, tmp ) == tmp ) return true; } }上面的 BOOST_INTERLOCKED_INCREMENT 和 BOOST_INTERLOCKED_COMPARE_EXCHANGE 都位于 boost 的 detail/interlocked.hpp 文件中,如大家所料,非常简单:
# define BOOST_INTERLOCKED_INCREMENT InterlockedIncrement
# define BOOST_INTERLOCKED_DECREMENT InterlockedDecrement
# define BOOST_INTERLOCKED_COMPARE_EXCHANGE InterlockedCompareExchange
这种变化意味着什么?最起码,lock free 算法为 boost 智能指针带来了非锁定语义。同时,由于 InterlockedCompareExchange 是一个 CAS (Compare And Swap) 操作,它可能由于读 - 写操作之间有其它线程介入而导致失败,为了能从这种失败中恢复过来, boost 1.33 代码中使用了那个 for(;;) 循环,也就是无限的重试以保证引用计数操作成功。在性能方面,由于 Interlocked 系列的系统调用都利用了硬件的原子操作,在性能上应该会比 scoped_lock 有一定的提高,但是循环所引入的额外操作又对性能有不良影响,究竟孰轻孰重,我们下面就在测试中检验。
测试程序非常简单,我尽量不引入无关的操作以免影响结果:
(代码文件:sp.cpp)
#include <ctime>
#include <iostream>
#include <boost/shared_ptr.hpp>
#include <boost/weak_ptr.hpp>
using namespace boost;
int main()
{
clock_t clk_s, clk_e;
shared_ptr<float> fp(new float(3.14f));
for(int i = 0; i < 10; ++i)
{
clk_s = std::clock();
for(int j = 0; j < 1000000; ++j)
{
shared_ptr<float> fp1 = fp;
}
clk_e = std::clock();
std::cout << clk_e - clk_s << std::endl;
}
std::cout << std::endl;
for(int i = 0; i < 10; ++i)
{
clk_s = std::clock();
for(int j = 0; j < 1000000; ++j)
{
weak_ptr<float> wp1 = fp;
}
clk_e = std::clock();
std::cout << clk_e - clk_s << std::endl;
}
}
我在同一台机器的两套环境下编译这个程序,这两套环境唯一的不同只在于一个使用 boost 1.32 ,一个使用 boost 1.33。编译命令是最简单的一条:
cl /EHsc sp.cpp
注意,这里没有用 /MT 开关,意味着我是在单线程状态下编译,后面会用另外的指令编译,比较其结果是饶有趣味的。
|
boost 1.32
|
boost 1.33
|
shared_ptr (10次,以空格分开)
|
150 60 50 60 50 60 50 60 70 60 |
220 80 90 80 80 90 81 80 90 90 |
weak_ptr (10次,以空格分开)
|
51 50 60 50 60 50 50 60 50 50 |
100 90 80 80 91 80 80 90 80 90 |
我知道一幅图抵得上一千个字,好在用 Excel 做个图也很轻易,下面是这些数据的图表显示:
可以看到,由于在单线程环境下,boost 1.32 的 shared_ptr 引用计数器默认操作实际上是
++use_count_;
而 boost 1.33 则实际上是
InterlockedIncrement( &user_count_ );
后者带来了平均 46.4% 的运行时间延长。对于 weak_ptr ,boost 1.32 中的操作是
if(use_count_ == 0) boost::throw_exception(boost::bad_weak_ptr());++use_count_;
而 boost 1.33 的操作是
for( ;; ){long tmp = static_cast< long const volatile& >( use_count_ );if( tmp == 0 ) return false;if( InterlockedCompareExchange( &use_count_, tmp + 1, tmp ) == tmp ) return true;}
后者带来的运行时间的增加平均达到 62.1% 。这的确是一个值得注意的问题。
接下来我换用另外的编译命令:
cl /EHsc /D"BOOST_HAS_THREADS" sp.cpp
这仍然是单线程,但是我通过定义 BOOST_HAS_TREADS 宏强制 boost 1.32 使用 scope_lock ,以模拟其在多线程下的表现,同时排除其他因素的干扰。
|
boost 1.32
|
boost 1.33
|
shared_ptr (10次,以空格分开)
|
380 190 211 190 200 190 201 190 200 191 |
230 90 80 90 80 90 81 100 80 90 |
weak_ptr (10次,以空格分开)
|
190 190 190 191 180 190 191 190 190 200 |
80 90 80 80 91 80 90 80 90 80 |
当然,还有图:
我们看到,定义 BOOST_HAS_TREADS 宏对于 boost 1.32 的智能指针性能影响是巨大的,而对于 boost 1.33 ,考虑到测量误差,可以说是没有影响。因为在这种情况下,boost 1.33 的引用计数操作没有变化,而 boost 1.32 的 shared_ptr 引用计数变成了
mutex_type::scoped_lock lock(mtx_);++use_count_;
而 weak_ptr 的引用计数变成了
mutex_type::scoped_lock lock(mtx_);if(use_count_ == 0) boost::throw_exception(boost::bad_weak_ptr());++use_count_;
也就是至少多出了一个 scoped_lock 的构造和析构成本,比起 boost 1.33 ,这个时候的 boost 1.32 shared_ptr 平均运行时间多出了约 112% ,weak_ptr 的平均运行时间多出了约 126.2% !我们终于看到了新的实现在性能上的好处。
结论:
新的 lock free 算法使得多线程环境下的 boost 智能指针性能大大提高,但是也带来了单线程环境下的一些性能成本,有没有办法两全其美呢?其实很简单,只要沿用过去的做法,用宏来区别即可:
void add_ref_copy() {#if defined(BOOST_HAS_THREADS) BOOST_INTERLOCKED_INCREMENT( &use_count_ );#else ++use_count_;
#endif
}
以及
bool add_ref_lock() // true on success {#if defined(BOOST_HAS_THREADS) for( ;; ) { long tmp = static_cast< long const volatile& >( use_count_ ); if( tmp == 0 ) return false; if( BOOST_INTERLOCKED_COMPARE_EXCHANGE( &use_count_, tmp + 1, tmp ) == tmp ) return true; }
#else
if(use_count_ == 0) return false; ++use_count_;
#endif
}
就可以保证在两种情况下的最优性能。
分享到:
相关推荐
### Boost智能指针详解 #### 一、智能指针概述 智能指针是C++中用于自动管理动态分配内存的一种工具。与原始指针不同,智能指针通过封装额外的操作来帮助开发者避免常见的内存泄漏和其他资源管理问题。Boost库提供...
matlab 2014b 破解缺少dll,boost_date_time-vc100-mt-1_49.dll,以及boost_filesystem-vc100-mt-1_49.dll
在这个名为"Boost智能指针示例源码"的压缩包中,我们有机会深入理解并学习如何在实际项目中使用这些智能指针。 首先,`shared_ptr`是Boost(也是C++11标准库)中的一个智能指针,它通过引用计数来管理对象的生命...
matlab 2016a 由于找不到 boost_iostreams-vc120-mt-1_56.dll,无法继续执行代码。重新安装程序可能会解决此问题。
Python Boost扩展模块是将C++的Boost库与Python语言相结合的一种工具,旨在增强Python的性能和功能。Boost库是一个开源的C++库集合,提供了大量的高效、跨平台的实用程序,包括数学算法、图形库、多线程支持、日期...
资源分类:Python库 所属语言:Python 资源全名:catboost-0.24.1-cp37-none-win_amd64.whl 资源来源:官方 安装方法:https://lanzao.blog.csdn.net/article/details/101784059
matlab 2016a 由于找不到 boost_filesystem-vc120-mt-1_56.dll,无法继续执行代码。重新安装程序可能会解决此问题。
离线安装包,亲测可用
**PyPI 官网下载 | catboost-0.6.2-cp34-none-win_amd64.whl** PyPI(Python Package Index)是Python社区的重要资源库,它为Python开发者提供了大量的开源软件包和模块,使得安装、分享和管理Python项目变得更加...
"boost-asio-cpp-network-programming-chinese.pdf"这个PDF文件很可能是Boost.Asio的中文教程,涵盖了从基础到高级的各种主题,对于初学者和有经验的开发者都是很好的参考资料。它可能会讲解如何设置服务器和客户端...
boost_python-1.76-cp310-cp310-win32
离线安装包,测试可用
Boost库包含了大量的模块,如智能指针、线程管理、函数对象绑定、正则表达式、图形算法等,极大地丰富了C++的编程语境。版本1.68是Boost的一个稳定版本,它包含了众多改进和修复,确保了在不同环境下的兼容性和稳定...
资源分类:Python库 所属语言:Python 资源全名:catboost-1.0.3-cp310-none-manylinux1_x86_64.whl 资源来源:官方 安装方法:https://lanzao.blog.csdn.net/article/details/101784059
离线安装包,测试可用
标题《xapp1206-boost-sw-performance-zynq7soc-w-neon.pdf》指向了这篇文档的核心内容,即如何利用NEON技术提升基于Zynq-7000 APSoC平台的软件性能。描述部分明确指出,这是Xilinx官方提供的开发手册,旨在指导...
资源来自pypi官网。 资源全名:catboost-0.24.2-cp27-none-manylinux1_x86_64.whl
离线安装包,测试可用
离线安装包,亲测可用