Last updated:2026-10-12
C++ string SSO Performance Test¶
Today I want to ask you two questions:
1. Is there any difference in performance between the following lines of code?
2. What is sizeof(std::string)?
Let's explore these two questions together.
Since the answers to both differ between g++ (libstdc++) and clang (libc++), each is analyzed separately below.
1. g++ (libstdc++)¶
For the g++ source code, we take g++-10 as the example; see this GitHub link for the detailed code — here the core parts are listed.
template<typename _CharT, typename _Traits, typename _Alloc>
class basic_string {
private:
struct _Alloc_hider : allocator_type {
pointer _M_p; // The actual data.
};
enum { __min_cap = 15 / sizeof(value_type) };
_Alloc_hider _M_dataplus; // 8 bytes
size_type __size_; // 8 bytes
union {
_CharT _M_local_buf[_S_local_capacity + 1]; // 16 bytes
size_type _M_allocated_capacity; // 8 bytes
}; // 16 bytes
}; // 32 bytes
In the code above, the comments spell out in detail the bytes a string occupies (note: value_type is counted as char here, i.e. sizeof(value_type) = 1).
sizeof(string) = 8 + 8 + 16 = 32.
That is 8 bytes of ptr, 8 bytes of size, and a 16-byte union.
Performance-wise, SSO is implemented here: the local buffer stores up to 15 characters, and once there are more than 15 characters the data is allocated on the heap. I wrote a benchmark to compare the performance.
- The short-string test input is "hello world hel", 15 characters in total.
- The long-string test input is "hello world helo", 16 characters in total.
As you can see, the performance gap is huge — a single character makes this much difference for std::string!
At its core, this is still the SSO local buffer mentioned earlier.
Benchmark: https://quick-bench.com/q/s7pvDB_SwQyDCcZk50odAaYIzMc

2. clang++ (libc++)¶
Likewise, we take llvm-14 as the example; if you want the complete code, follow the GitHub link below — here the core parts are excerpted.
https://github.com/llvm/llvm-project/blob/release/14.x/libcxx/include/string#L765
template<class _CharT, class _Traits, class _Allocator>
class basic_string {
private:
struct __long {
size_type __cap_; // 8 bytes
size_type __size_; // 8 bytes
pointer __data_; // 8 bytes
};
enum {__min_cap = (sizeof(__long) - 1)/sizeof(value_type) > 2 ?
(sizeof(__long) - 1)/sizeof(value_type) : 2};
struct __short {
union {
unsigned char __size_; // 1 bytes
value_type __lx; // 1 bytes
}; // 1 bytes
value_type __data_[__min_cap]; // 23 bytes
}; // 24 bytes
union __ulx {
__long __lx; // 24 bytes
__short __lxx; // 24 bytes
}; // 24 bytes
}
In the code above, the comments spell out in detail the bytes a string occupies (note: value_type is counted as char here, i.e. sizeof(value_type) = 1).
sizeof(string) equals the size of the union, and this union is 24 bytes, so it is 24.
As for SSO, the local buffer holds no more than 22 characters; beyond 22 characters the data is allocated on the heap.
So when we run on clang the same test code we used for gcc, we get rather different results: the long string performs about the same as the short one. The reason is that the gcc test cases are all within 22 characters — all short strings.
Benchmark: https://quick-bench.com/q/nmLMqQZ31cmWBbbIFr2nHN2JL1k

When we test genuinely long and short strings, performance changes noticeably, with the trend matching gcc.
- The short-string test input is "hello world hello worl", 22 characters in total.
- The long-string test input is "hello world hello world", 23 characters in total.
Benchmark: https://quick-bench.com/q/ujtMrtotEi6RrpfMDJ1UcSLFIJE

Related Topics¶
- memcpy vs std::copy Performance Comparison | C++那些事
- C++ Polymorphism: Virtual Functions, CRTP, and variant | C++那些事