5 ms·
Bit of a nitpick, but the short string optimization example seems a bit weird to me? I thought SSO normally consisted of a union of the buffer and the regular d
by aw1621107 2y ago
Bit of a nitpick, but the short string optimization example seems a bit weird to me? I thought SSO normally consisted of a union of the buffer and the regular data/size/capacity pointers with some kind of in-band signaling, rather than the three pointers and the SSO buffer with the data pointer always pointing to the string data. The latter seems to somewhat defeat the purpose of SSO - the point is that for short strings you don't want to have to chase a pointer, so having the data pointer point to the inline buffer seems odd.
In fact, the std::is_trivially_relocatable paper explicitly lists std::string from libc++ and MSVC as being trivially relocatable [0], which seems to further weaken the example.
That's not to distract from the main point - that self-referential types aren't trivially relocatable - but I feel the example could be better.
[0]: https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2024/p1144r10.html https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2024/p11...
- saagarjha 2y agoTypically, yes. However the implementation you’ve described is still an optimization sometimes because you can fit all the data together.
- quuxplusone 2y agoWell, libstdc++'s std::string does SSO as depicted in the post. So you're right — 2 out of 3 library vendors manage to do SSO without losing trivial relocatability — but consider that 1 in 3 vendors does lose trivial relocatability thereby. Now consider a type that is like std::string but doesn't do SSO — say, std::vector or std::unique_ptr or even std::shared_ptr. Those types are trivially relocatable on 3 out of 3 vendors, no problem. So I have no problem with the post's example. Looking forward to part 2!
- aw1621107 2y agoAh, so it does (broadly speaking, at least), and I stand corrected. That was negligent of me to not check. Does libstdc++'s implementation offer advantages that libc++/MSVC's lack? The implementation still looks weird to me, but I've got to be missing something if people far smarter than me decided on that representation.
- quuxplusone 2y agoAs far as I know, libstdc++'s representation has two advantages: First, it simplifies the implementation of `s.data()`, because you hold a pointer that invariably points to the first character of the data. The pointer-less version needs to do a branch there. Compare libstdc++ [1] to libc++ [2]. [1]: https://github.com/gcc-mirror/gcc/blob/065dddc/libstdc++-v3/include/bits/basic_string.h#L225-L228 https://github.com/gcc-mirror/gcc/blob/065dddc/libstdc++-v3/... [2]: https://github.com/llvm/llvm-project/blob/1a96179/libcxx/include/string#L1936-L1938 https://github.com/llvm/llvm-project/blob/1a96179/libcxx/inc... Basically libstdc++ is paying an extra 8 bytes of storage, and losing trivial relocatability, in exchange for one fewer branch every time you access the string's characters. I imagine that the performance impact of that extra branch is tiny, and massively confounded in practice by unrelated factors that are clearly on libc++'s side (e.g. libc++'s SSO buffer is 7 bytes bigger, despite libc++'s string object itself being smaller). But it's there. The second advantage is that libstdc++ already did it that way, and to change it would be an ABI break; so now they're stuck with it. I mean, obviously that's not an "advantage" in the intuitive sense; but it's functionally equivalent to an advantage, in that it's a very strong technical answer to the question "Why doesn't libstdc++ just switch to doing it libc++'s way?"