小对象优化

小对象优化是一种在低级数据结构中使用的技术,例如 std::string(有时称为短/小字符串优化)。这意味着使用堆栈空间作为缓冲区而不是一些已分配的内存,以防内容小到足以容纳在保留空间内。

通过添加额外的内存开销和额外的计算,它会尝试防止昂贵的堆分配。这种技术的好处取决于使用情况,如果使用不当甚至会损害性能。

使用此优化实现字符串的一种非常天真的方法如下:

#include <cstring>

class string final
{
    constexpr static auto SMALL_BUFFER_SIZE = 16;

    bool _isAllocated{false};                       ///< Remember if we allocated memory
    char *_buffer{nullptr};                         ///< Pointer to the buffer we are using
    char _smallBuffer[SMALL_BUFFER_SIZE]= {'\0'};   ///< Stack space used for SMALL OBJECT OPTIMIZATION

public:
    ~string()
    {
        if (_isAllocated)
            delete [] _buffer;
    }        

    explicit string(const char *cStyleString)
    {
        auto stringSize = std::strlen(cStyleString);
        _isAllocated = (stringSize > SMALL_BUFFER_SIZE);
        if (_isAllocated)
            _buffer = new char[stringSize];
        else
            _buffer = &_smallBuffer[0];
        std::strcpy(_buffer, &cStyleString[0]);
    }

    string(string &&rhs)
       : _isAllocated(rhs._isAllocated)
       , _buffer(rhs._buffer)
       , _smallBuffer(rhs._smallBuffer) //< Not needed if allocated
    {
        if (_isAllocated)
        {
           // Prevent double deletion of the memory
           rhs._buffer = nullptr;
        }
        else
        {
            // Copy over data
            std::strcpy(_smallBuffer, rhs._smallBuffer);
            _buffer = &_smallBuffer[0];
        }
    }
    // Other methods, including other constructors, copy constructor,
    // assignment operators have been omitted for readability
};

正如你在上面的代码中所看到的,为了防止某些 newdelete 操作,添加了一些额外的复杂性。除此之外,该类具有更大的内存占用,除非在几种情况下可能不会使用。

通常尝试使用位操作在指针 _buffer 内编码 bool 值 _isAllocated 以减小单个实例的大小(intel 64 位:可以将大小减小 8 个字节)。优化只有在知道平台的对齐规则时才可能。

什么时候用?

由于此优化会增加许多复杂性,因此不建议在每个类上使用此优化。在常用的低级数据结构中经常会遇到它。在常见的 C++ 11 standard library 实现中,可以在 std::basic_string<>std::function<>找到用法。

由于此优化仅在存储的数据小于缓冲区时阻止内存分配,因此只有在类通常与小数据一起使用时才会带来好处。

这种优化的最后一个缺点是移动缓冲区需要额外的努力,使得移动操作比不使用缓冲区时更昂贵。当缓冲区包含非 POD 类型时尤其如此。