The other is that almost no library is written in such a way that buffer re-use is possible (looking at you, typical kafka clients that throw off a buffer of garbage per message and protobuf). The latter could be fixed if people paid more attention to returning buffers to the caller.
Granted the latter leads to more verbose code and chaining of several calls is no longer possible.
But I am puzzled that even performance-oriented libraries both in Go and Rust still prefer to allocate the results themselves.
It may be that you can "just" build one, but you can't "just" use it and expect any of the available libraries and built ins to work with it.
How many things would you have to "just" rewrite?
(Yes: I used arenas a lot when I was shipping C code; they're a very easy way to get big speed boosts out of code that does a lot of malloc).
One could imagine a language that allows syntax like CallWithArena(functionPointer, someSetupInfo) and any standard library allocation therein would use the arena, releasing on completion or error.
Languages like Python and modern Java would typically use a thread/virtualthread/greenlet-local variable to track the state for this kind of pattern. The fact that Go explicitly avoids this pattern is a philosophical choice, and arguably a good one for Go to stick to, given its emphasis on avoiding the types of implicit "spooky action at a distance" that often plague hand-rolled distributed systems!
But the concept of arenas could still apply in an AlternateLowLevelLanguage where a notion of scoped/threaded context is implicit and language-supported, and arena choice is tracked in that context and treated as a first-class citizen by standard libraries.
> Operations such as new, free and delete by default will use context.allocator, which can be overridden by the user. When an override happens all called procedures will inherit the new context and use the same allocator.
[0] https://standards.scheme.org/corrected-r7rs/r7rs-Z-H-6.html#...
This is something I look forwards to exploring later in my current pet project, right now it has possibly the stupidest GC (just tracks C++ 'new' allocated objects) but is set up for drop in arena allocation with placement new so, we'll see how much that matters later on. There are two allocation patterns, statements and whatnot get compiled to static continuation graphs which push and pop secondary continuations and Value objects to do the deed so, I believe, the second part with the rapid temporary object creation will see the most benefit.
Anyhoo, slightly different pattern where the main benefits will most likely be from the cache locality or whatever, assuming I can even make a placement new arena allocator which is better than the performance of the regular C++ new. Never know, might even add more overhead than just tracking a bunch of raw C++ pointers as I can't imagine there's even a drop of performance which C++ new left on the table?
There are plenty of specialisations that get more performance out, e.g. multi-threaded code in NUMA architectures.
The same ones you'd have to rewrite using the (experimental) arenas implementation found in the standard library. While not the only reason, this is the primary reason for why it was abandoned; it didn't really fit into the ecosystem the way users would expect — you included, apparently.
"Memory regions" is the successor, which is trying to tackle the concerns you have. Work on it is ongoing.