跟我学c++中级篇——小对象优化系列

一、SBO/SOO/SSO

Small Buffer Optimization, SBO,小缓冲优化(其它类似还有small object optimization,小对象优化;SOO;small size optimization,SSO,小尺寸优化)。它们虽然叫法略有不同,但是基本目的都是一样的。在c++,如果编写一个通用类来包含其它一些具体的实现,就会出现一个问题,这个类到底有多大?假设它很大,必须在堆上。但是如果实际情况它在某种场景下会变得很小(只有十几个字节甚至更小),那直接分配在堆上,会不会引起内存碎片。但是只考虑这一种情况,就直接分配在栈上,遇到大对象又该如何?基于这个问题,c++才引入了上述的这些个SBO,SOO,SSO。可以统称为小对象优化技术。
为了能够实现优化,一般来说会在这个类中增加一个域,此域专门来存放小对象(直接放在栈上)。但当这个域很大时,就无法用栈来实现,那么可以存放一个指针让其在堆上实现。而这种复用的数据结构,有过c/c++经验的首先就会想到UNION联合体来实现,确实也是如此。但是,这种情况是无法使用普通的多态来实现VTABLE动态调用,那么只能通过函数指针来实现类似多态的行为来达到目的。

二、源码示例

在STL中,经常可以看到类似下面的代码:

union _Bxty
{   // storage for small buffer or pointer to larger one
   _Elem _Buf[_BUF_SIZE];
   _Elem *_Ptr;
   char _Alias[_BUF_SIZE];  // to permit aliasing
} _Bx;

  union _Nocopy_types
  {
    void*       _M_object;
    const void* _M_const_object;
    void (*_M_function_pointer)();
    void (_Undefined_class::*_M_member_pointer)();
  };

是不有刚刚描述的味道,确实是如此。再看一下相关的实现的代码:

class _Undefined_class;

union _Nocopy_types
{
  void*       _M_object;
  const void* _M_const_object;
  void (*_M_function_pointer)();
  void (_Undefined_class::*_M_member_pointer)();
};

union _Any_data
{
  void*       _M_access()       { return &_M_pod_data[0]; }
  const void* _M_access() const { return &_M_pod_data[0]; }

  template<typename _Tp>
    _Tp&
    _M_access()
    { return *static_cast<_Tp*>(_M_access()); }

  template<typename _Tp>
    const _Tp&
    _M_access() const
    { return *static_cast<const _Tp*>(_M_access()); }

  _Nocopy_types _M_unused;
  char _M_pod_data[sizeof(_Nocopy_types)];
};
class _Function_base
{
public:
static const std::size_t _M_max_size = sizeof(_Nocopy_types);
static const std::size_t _M_max_align = __alignof__(_Nocopy_types);

template<typename _Functor>
class _Base_manager
{
protected:
static const bool __stored_locally =
(__is_location_invariant<_Functor>::value
&& sizeof(_Functor) <= _M_max_size
&& __alignof__(_Functor) <= _M_max_align
&& (_M_max_align % __alignof__(_Functor) == 0));

typedef integral_constant<bool, __stored_locally> _Local_storage;

// Retrieve a pointer to the function object
static _Functor*
_M_get_pointer(const _Any_data& __source)
{
const _Functor* __ptr =
__stored_locally? std::__addressof(__source._M_access<_Functor>())
/* have stored a pointer */ : __source._M_access<_Functor*>();
return const_cast<_Functor*>(__ptr);
}

// Clone a location-invariant function object that fits within
// an _Any_data structure.
static void
_M_clone(_Any_data& __dest, const _Any_data& __source, true_type)
{
new (__dest._M_access()) _Functor(__source._M_access<_Functor>());
}
......
static void
_M_destroy(_Any_data& __victim, false_type)
{
delete __victim._M_access<_Functor*>();
}

public:
static bool
_M_manager(_Any_data& __dest, const _Any_data& __source,
_Manager_operation __op)
{
switch (__op)
{
#ifdef __GXX_RTTI
case __get_type_info:
__dest._M_access<const type_info*>() = &typeid(_Functor);
break;
#endif
case __get_functor_ptr:
__dest._M_access<_Functor*>() = _M_get_pointer(__source);
break;

case __clone_functor:
_M_clone(__dest, __source, _Local_storage());
break;

case __destroy_functor:
_M_destroy(__dest, _Local_storage());
break;
}
return false;
}

static void
_M_init_functor(_Any_data& __functor, _Functor&& __f)
{ _M_init_functor(__functor, std::move(__f), _Local_storage()); }
......
};
template<typename _Res, typename... _ArgTypes>
class function<_Res(_ArgTypes...)>
: public _Maybe_unary_or_binary_function<_Res, _ArgTypes...>,
private _Function_base
{
typedef _Res _Signature_type(_ArgTypes...);

template<typename _Functor>
using _Invoke = decltype(__callable_functor(std::declval<_Functor&>())
(std::declval<_ArgTypes>()...) );

template<typename _CallRes, typename _Res1>
struct _CheckResult
: is_convertible<_CallRes, _Res1> { };
......
};

从上面的代码可以看到,function继承了_Function_base,而在_Function_base中有一个__stored_locally ,这经的目的就是对小对象优化的一个处理判断,看它的代码typedef integral_constant<bool, __stored_locally> _Local_storage,通过其来具体来以函数指针动态跳到指定的句柄中去。换句话说,是使用本地的存储还是在Heap上创建。注意看一下两个联合体。
虽然说维护着一个类似VTABLE的东西可能会增加复杂性拖慢编译速度,但它香啊,关键时刻真能提高效率节省内存啊。或许这就是它的魅力所在吧。在STL库中,std::string ,std:function,std::any中都有类似的代码,可以看一下,进行深入的理解和分析。

三、总结

SBO和类型擦除密不可分,在后面的类型擦除时会进一步将其应用进行说明。c++做为一门早期的底层语言,对性能和内存的应用还是非常注意的。其实这种小对象优化技术,就是一个重要的体现。所以才会让很多初学者在学习c++时感到非常痛苦。一个非常简单的东西为什么写得如此复杂?其实就是为了这个。但是这些优化技术因为复杂往往让初学者无所适从,从而进一步产生厌学心态。这也是c++在大学和培训学校里一般都不再开设的一个重要原因。更重要的是,c++这种复杂的技术或者说小技巧又有好多类好多种。从老版本迭代到新版本,既有老的手动实现又有STL库的封装实现。如果真得没有多年的经验,确实容易产生一种两眼一摸黑的现象。
如何解决这个问题呢?对于确实想学习c++的人来说,抓住一点,消灭一点。伤其十指,不如断其一指。不断积累,勤学不辍。这就是捷径,厚积而薄发。最后最重要的一点提示,要多看高手的代码,多写代码,多实践!

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值