小对象优化
小对象优化是一种在低级数据结构中使用的技术,例如 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
};
正如你在上面的代码中所看到的,为了防止某些 new
和 delete
操作,添加了一些额外的复杂性。除此之外,该类具有更大的内存占用,除非在几种情况下可能不会使用。
通常尝试使用位操作在指针 _buffer
内编码 bool 值 _isAllocated
以减小单个实例的大小(intel 64 位:可以将大小减小 8 个字节)。优化只有在知道平台的对齐规则时才可能。
什么时候用?
由于此优化会增加许多复杂性,因此不建议在每个类上使用此优化。在常用的低级数据结构中经常会遇到它。在常见的 C++ 11 standard library
实现中,可以在 std::basic_string<>
和 std::function<>
中找到用法。
由于此优化仅在存储的数据小于缓冲区时阻止内存分配,因此只有在类通常与小数据一起使用时才会带来好处。
这种优化的最后一个缺点是移动缓冲区需要额外的努力,使得移动操作比不使用缓冲区时更昂贵。当缓冲区包含非 POD 类型时尤其如此。