小物件優化

小物件優化是一種在低階資料結構中使用的技術,例如 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 型別時尤其如此。