A memory-saving type for Collections of Strings

I made a pull request for a new Collection type. Like many containers with variable element counts, String stores its character data in a dynamic memory block. Since the standard collections do the same thing, a collection of strings ends up with several allocated blocks. The way around that is a specialized collection that mushes all the allocations together. That's what StringCollection does.

String doesn't always heap allocate. For example for immortal static strings that are known at compile time, or for Strings of 15 or less utf8 bytes (usually).

What is your usecase for this type? Given the inherent restrictions it comes with.

How is this type different from just using a String as the collection, and Substrings of that String as the subsequnces?

It would be nice to know concrete usecases and issues solved by this type, and optimally we'd have benchmarks (more optimally, benchmarks we can run / reproduce too), that are not too specific to certain usecases (the type needs to perform adequately at all/most times).

4 Likes

Interesting!

The most immediate issue with this is that String does not allow its storage to come from slab-allocated storage, so this collection needs to initialize its elements anew, each time we access them. That isn't great: it makes this a heavyweight collection type, and it can also be viewed as working against the memory-saving benefits -- after all, data is literally copied out into a new String instance every time we access the contents.

Relatedly, the current implementation uses UTF-32 for its internal representation. That choice is also incompatible with the goal for optimizing memory use, and it additionally requires needless encoding conversions on access. The right move is to generally store text as UTF-8 data, like native Swift String instances already do.

I concur with @MahdiBM: it may be more viable to use Substring as the element type, with String itself serving as the storage representation. For iteration purposes, element boundaries can be stored out of band, e.g., as an array of string indices. As of Swift 5.7, Substring runs its own grapheme breaking independent of the wider context of the String it comes from, so neighboring pieces of text will not affect the Character contents of "StringCollection" elements.

Another (probably even more desirable) option would be to embrace the ownership model and implement this as a pseudo-container with borrowed UTF8Span elements. (It cannot be a real Container yet, as we can't generally model nonescapable elements in current Swift. But it is possible to implement the special case of a container-like type whose nonescapable elements borrow the container itself, and it would be really useful to see one in actual practice.))

3 Likes