This answer gives a nice high-level overview of short string optimization (SSO). However, I would like to know in more detail how it works in practice, specifically in the libc++ implementation:
How short does the string have to be in order to qualify for SSO? Does this depend on the target architecture?
How does the implementation distinguish between short and long
strings when accessing the string data? Is it as simple as m_size <= 16
or is it a flag that is part of some other member variable? (I
imagine that m_size
or part of it might also be used to store
string data).
I asked this question specifically for libc++ because I know that it uses SSO, this is even mentioned on the libc++ home page.
Here are some observations after looking at the source:
libc++ can be compiled with two slightly different memory layouts for the string class, this is governed by the _LIBCPP_ALTERNATE_STRING_LAYOUT
flag. Both of the layouts also distinguish between little-endian and big-endian machines which leaves us with a total of 4 different variants. I will assume the "normal" layout and little-endian in what follows.
Assuming further that size_type
is 4 bytes and that value_type
is 1 byte, this is what the first 4 bytes of a string would look like in memory:
// short string: (s)ize and 3 bytes of char (d)ata
sssssss0;dddddddd;dddddddd;dddddddd
^- is_long = 0
// long string: (c)apacity
ccccccc1;cccccccc;cccccccc;cccccccc
^- is_long = 1
Since the size of the short string is in the upper 7 bits, it needs to be shifted when accessing it:
size_type __get_short_size() const {
return __r_.first().__s.__size_ >> 1;
}
Similarly, the getter and setter for the capacity of a long string uses __long_mask
to work around the is_long
bit.
I am still looking for an answer to my first question, i.e. what value would __min_cap
, the capacity of short strings, take for different architectures?
Other standard library implementations
This answer gives a nice overview of std::string
memory layouts in other standard library implementations.