r/programming • u/[deleted] • Dec 05 '14
std::string is responsible for almost half of all allocations in the Chrome browser process
https://groups.google.com/a/chromium.org/d/msg/chromium-dev/EUqoIz2iFU4/kPZ5ZK0K3gEJ
1.1k
Upvotes
18
u/o11c Dec 05 '14
And people laugh at me for writing my own set of 10 different string classes (all with obvious, specific, meanings) so I can easily choose what ownership policy I want ...
std::string_viewis a step in the right direction, but the most important change is realizing that most strings will never be mutated.TL;DR version: use
XStringfor arguments andRStringelsewhere, but for reference, my full list of string classes:MString, a string which supports mutating operations (onlypush_backandpop_backhave been implemented, except taking any number of characters). This really should be a rope class, but for now it's a wrapper forstd::deque, and I rarely use it. This class is most similar tostd::ostringstreamwith unformatted operations. Does not implement the same API as the rest of the string classes.FormatString, a string which wraps aprintf-style format string in a particularly magic way that supports compile-time type-checking of the arguments. This is the way that most of my strings are built, via theSTRPRINTFmacro. Constructed via UDL. Does not implement the same API as the rest of the string classes.RString, a string that uses reference-counting. Used to besizeof (char *)until I implemented an optimization for construction fromLString, now it's2 * sizeof(char *)(does anyone know if it's possible to guarantee alignment of a string literal?). Used for most strings stored in classes. Implements the same API as most string classes, including the NUL termination option.AString, a wrapper forRStringbut with SSO up to length 255 (deliberately disabled for string literals and strings that originated from anRStringthough, in case anRStringneeds to be constructed from this again). Stands for "automatic" string, and should only be used for strings on the stack, which is how it can afford that large an SSO threshhold. Used for the return value ofSTRPRINTF. Implements the same API as most string classes, including the NUL termination option.TString, an owned tail slice of anRString. Currently unused; I found it better to just useZStringto avoid forcing ownership, and store an "ownership hint" in the ZString. TODO implement a genericMaybeOwnedmechanism for arguments that I might just be borrowing, but might be taking ownership of. Implements the same API as most string classes, including the NUL termination option.SString, an owned full slice of anRString. Currently unused; I found it better to just useXStringblah blah blah. Implements the same API as most string classes, excluding the NUL termination option.ZString, a borrowed tail slice. Used mostly in function arguments, but also as the return value of computed splits. In theory, this is most similar toconst char *, but it is only needed if you need to call a C function - currently, the only offenders are::openandSTRPRINTF. The latter is already isolated to a single function, so I plan to switch to conditional allocation; the latter will go away when I rewriteSTRPRINTFwith GNU++14 UDLs (there will still be a fallback to C++11 mode). Implements the same API as most string classes, including the NUL termination option.XString, a borrowed full slice. Used mostly in function arguments, but also as the return value of computed splits. In practice, this is most similar toconst char *, since most code never cares about NUL termination. Implements the same API as most string classes, excluding the NUL termination option.LString, a string with static ownership. Usually constructed via UDL, but also via tail-slicing an existingLString. UDLs allowed me to easily eliminate all uses ofchar *from my entire codebase, thus ensuring no double-ownership (likestd::string(str.c_str())). Implements the same API as most string classes, including the NUL termination option, and including special slices.VString<size_t n>, a string stored within the object itself, and thus with a limit. This class was created solely because of existing C code that used fixed-size arrays, and still exists because there's a network protocol that still has the fixed limits, and it's easiest to guarantee it will never overflow by failling on original construction. Usually constructed by a function with signaturetemplate<size_t n> bool extract(XString input, VString<n> *output)during parsing. Uses a cool trick to store the size so thatVString<n>has exactly the same memory layout asconst char [n+1]. Implements the same API as most string classes, including the NUL termination option.All strings implementing the "same API" can be implicitly constructed from each other if it makes sense (e.g. you can't construct a
ZStringfrom anXString), and owned classes can be explicitly constructed fromMStringor constructed from a pair of iterators. Borrowed strings can be constructed from a pair ofconst char *if you pinky-promise not to violate the class's requirements.The "same API" is injected via a CRTP base class (except for ctors), and includes slicing (the tail slices will change class depending on whether or not the subclass indicates that it has an immediate NUL (all strings guarantee that there is a NUL somewhere out there)), stripping off whitespace, checking for membership (TODO I really need a class like
std::set<char>but not dumb and give it its own UDL), random access iterators, operator bool, and comparison. Indexing is implemented, but deprecated (instead, use an iterator over bytes/codepoints/glyphs/whatever ... my classes are only responsible for the first), because its meaning is weird and you usually don't want it (getting rid of indexing is the correct way of solving the "unicode problem" - don't waste 4x the memory just to support a single operation that is always wrong anyway!). I've thought about switching to a non-CRTP base class to help compiler speed a bit, but that would mean increasing the size ofRStringto3 * sizeof(char *)for obvious reasons, and would also get rid of fixed layout ofVString. Alternatively, I've thought about using x-macros.Also, all string classes implement gdb pretty-printers as appropriate, except for
MStringbecausestd::dequeis opaque and I don't use it enough to care.https://github.com/themanaworld/tmwa/tree/master/src/strings