Comment by kllrnohj
5 days ago
Arena allocators are not some grand new concept. They're already commonly used in C++ in the places it makes sense to use them. Which is really not that many places, it's a fast but rather niche optimization. There's not a whole lot of scenarios where lots of temporary memory is needed for one well defined scope.
Video games are large and have lots of state and lots of threads. Zig's lack of ownership here with fully manual memory management is overall a poor fit.
I disagree with a lot of what you said, but I don't feel authorative enough to say you're wrong.
> Which is really not that many places, it's a fast but rather niche optimization. There's not a whole lot of scenarios where lots of temporary memory is needed for one well defined scope.
Arena allocators are not niche optimizations, or not something picked first for optimization. Contrary to what you said, arenas are useful for temporary allocations with poorly defined intermediate scope or lifetime (think functions directly or indirectly called by the arena owner). If the scope is local and well-defined, a regular allocator or even a fixed buffer would do just fine.
> Zig's lack of ownership
Zig doesn't have explicit annotations for it, but the concept of ownership and lifetime doesn't go away. It's not enforced by the compiler, which is an intentional tradeoff to let the programmer have more control and freedom. When you use languages with manual memory management, it's expected that you are capable of designing sensible programs in such a way that ownership and lifetimes are tractable and are part of the program design, rather than something to workaround to please the compiler.
> Zig doesn't have explicit annotations for it, but the concept of ownership and lifetime doesn't go away. It's not enforced by the compiler, which is an intentional tradeoff to let the programmer have more control and freedom.
Right, it's exactly like C, and we kinda all know how that worked out in practice already...
Hence why I called Zig a "love letter to C". If all you want is C with a dash of zest, that's Zig. If you want a modern language that has learned from the many hard lessons the industry has dealt with over the years... well, Zig ain't it. Which is a perfectly fine thing for Zig to be, it doesn't have to be a good general purpose language. We have plenty of those already from Rust to Go to Java/C#/Kotlin to etc...
> arenas are useful for temporary allocations with poorly defined intermediate scope or lifetime (think functions directly or indirectly called by the arena owner).
Arenas are not good for that because the arena as a whole has to outlive all of those poorly defined scopes & lifetimes, which is hard to do. Especially if you later go add on something like an retry-with-backoff or asynchronous metrics/tracing or caching or whatever. Then suddenly you're either fighting use-after-frees or doing deep-copying of data.
> Right, it's exactly like C, and we kinda all know how that worked out in practice already...
Production operating systems have been written in C, along the with the countless tooling, libraries and game engines (which you said are a poor fit for manual memory management) that modern systems depend on. I say it worked out it pretty well.
And zig did learn from the hard lessons from the industry and fixes a lot of problems with C. It also has a lot of affordances that makes it more than suitable for general purpose use.
> Arenas are not good for that because the arena as a whole has to outlive all of those poorly defined scopes & lifetimes, which is hard to do.
I don't what else to tell you, arenas outliving temporary allocations is exactly what it is made for, they go poof as soon as the arena owner is done. That's not hard, it makes it easier if anything. To give concrete examples, arenas are used on HTTP requests that are clean up in one go as soon as the request is done. They are also used on (possibly deep) recursive functions that are cleaned up as soon as the root function returns. Of course, you don't store arena-allocated memory elsewhere that outlives the arena, that would be dumb.
That's why you have to be consciously aware of the ownership and lifetimes that a piece of memory has. Ownership and lifetimes are just one part of the API contract of a function or module. You break it, that's on you. Having a compiler help with ownership model would be nice, but it's a not substitute for having a good mental model of your programs. It's not that different from the tradeoff of a having a less strict type system. Not every sanity check can or has to be performed at compile time. Zig also has debug allocators that catches a lot of memory mismanagement during testing. Hard to debug double-frees, use-after-frees and other things are a symptom of poor cavalier YOLO programming.
That all said, I do agree that manual memory management is really hard to do if you are used to just sweeping gigabytes of memory under rug, hoping the GC vacuum cleaner slurps it afterwards. It takes a mindset and a set of practice. But once you internalized it, it becomes second nature.
(Not to sound like a zig fanboy, I do think it's still rough around the edge and there are a lot of things I don't like. But manual memory management is not that big of a problem).
1 reply →