← Back to context

Comment by jstimpfle

1 day ago

They're not useable for anything serious, i.e. high throughput, low frequency, massively concurrent work. In other words, most of the things for which you shouldn't better have chosen a different language in the first place.

They're also unusable by the way because of ergonomic and software architecture factors, such as bad modularity, terrible compile times, unreadable error messages, unreadable symbol names...

Yes that is overgeneralizing a little bit but it's largely true.

The problem is typically not the containers themselves but all the other bad decisions that they push you to make in order to work around their "small issues".

The huge problem is that these containers can get you started quickly, i.e. leetcode type stuff and single threaded stuff, but at some point you'll realize your architecture ended up completely in the wrong place because of that.

If you haven't been thinking deeply about memory management and concurrency, you won't be able to understand, no offense meant. I've just fixed another subsystem that was completely overwhelmed, seeing 8x bandwidth gains already on a small testsystem, but the factor is basically unbounded when moving to bigger systems, when it's about contended vs uncontended.

> anything serious, i.e. high throughput, low frequency, massively concurrent work

Why is only 'high throughput, low frequency, massively concurrent work' considered 'serious'?

  • They're just clearly inferior in pretty much any situation.

    The map stuff the other posters summed up well but even std::vector is dogshit with pretty much all implementations having inlined grow code in push_back, a not too great API and missed optimisations e.g. no trivial relocation when growing the vector / moving it and no useful APIs such as "grow but don't initialise"...

    • To be fair grow-but-don't-initialize is a pretty fundamental part of the API, the reserve() method.

      But already the basic premise that you should push back without thinking is wrong. You will suffer reallocations and invalisations when you least expected them, and frankly you have to architect around that fact which is a terrible restriction. You can work around by pre reserving but at that point it's just a basic fixed heap allocated array but worse because the type gives you a weird look all the time, "I'll realloc as soon as you don't pay attention, harhar"!

      5 replies →

  • You are free to make your own definition, what are your suggestions?

    (Obviously I meant to say low latency, not low frequency)

    • That is a cop-out. You made the assertion, you define it.

      What about these particular workloads (and the environments they're used in) make them 'serious' and why are other workloads 'lesser' and therefore the standard library 'suffices'? Why not use better containers for everything? Google, for instance, universally recommends Abseil.

      1 reply →

If you haven't been thinking deeply about memory management and concurrency, you won't be able to understand, no offense meant

I do think about that, when needed. My point is that these containers can be 'good enough' in places where it doesn't really matter, not that they're always the go-to thing. E.g. I really don't see any issue using a map as part of a configuration type of object which gets read from args or json and which only gets used once at application start.