23 ms·
Untangling Lifetimes: The Arena Allocator
- kitd 4y agoThe author may be interested in Zig, where allocators are first-class components of the system. https://ziglang.org/documentation/0.9.1/#Choosing-an-Allocator https://ziglang.org/documentation/0.9.1/#Choosing-an-Allocat... There is even an arena allocator provided (amongst others) by the std lib.
- doesntmeananyth 4y agoYou may also be interested in this comment thread from the article page, where the author explains his opinion on Zig's approach to allocators: https://www.rfleury.com/p/untangling-lifetimes-the-arena-allocator/comment/9320539 https://www.rfleury.com/p/untangling-lifetimes-the-arena-all...
- jcparkyn 4y agoI don't use zig, but it seems to me that functions can choose exactly which type of allocator they want to accept (just by changing the type of the allocator parameter), making his point invalid.
- dottedmag 4y agoIt is telling about the mean knowledge level of C in the vicinity of the author that first paragraph does not include «why subject yourself to a language with such a pervasive UB?»
- kosolam 4y agoAnyone read it all and can tldr any interesting insights?
- IshKebab 4y agoThis came up before. TL;DR: If you use arena allocators everywhere then you won't have any memory errors! I think the idea is a really interesting one but it would be better if he didn't wrap it in typical "I don't make mistakes" hubris. I mean I guess at least this time he has some more reason than "because I'm not stupid" but I think he's still wrong. His technique probably reduces the chance of memory errors, but there's still nothing checking his work so he's still going to make mistakes. The other issue he doesn't address is resource allocation. It's fine to just drop the memory of a lot of things but sometimes you have to close files, handles, etc. RAII handles that perfectly. I'm not sure about this. I would say RAII and Rust's borrow checker are still superior but it's definitely true that they don't work well with arenas.
- zozbot234 4y ago> I would say RAII and Rust's borrow checker are still superior but it's definitely true that they don't work well with arenas. Note that this will definitely improve in future versions of Rust. Local allocators are an unstable feature already, and can support arena allocation.
- williamcotton 4y agoAs a C programmer who uses memory arenas quite frequently can you expand on what you know about Rust’s current or future support for such a feature? I’d like to start writing new projects in Rust and I’d like to continue to use this approach to memory management!
- creata 4y agoYou can of course use arenas in Rust just like in C. I think the parent was talking about support for using arenas as the memory for stuff like built-in containers (dynamic arrays, hash maps, etc.): https://github.com/rust-lang/wg-allocators https://github.com/rust-lang/wg-allocators
- williamcotton 4y ago
- quickthrower2 4y agoI am not a C programmer but I will try! Malloc/free - can turn into a tangled mess as you need to keep symmetry, and apps might have complex dep. graphs Stack - wonderfully simple but often if you need to share things across the depths of your program you need to define variables in shallower parts - but you won’t know how much is needed ahead of time. Example a parsing library. Arena - like a single malloc / free with it’s own stack. Use this to allocate memory for objects that can be all freed at the same time (if they need to be freed at all). No need to malloc for each object.
- omnicognate 4y agoIt's a good technical explanation of how arena allocation (a useful and easily googleable memory management technique) works in practice, in C, along with: * A lot of arguing that all other ways of dealing with the problem (garbage collectors, RAII, etc) are misguided and make for worse programs. This is a lot more controversial than "arena allocation is useful" but I have some sympathy for it. He's not alone in thinking it. See the "Handmade Network" which this guy is associated with. * A brief and unnecessary bit of free market fundamentalist politics, which seems par for the course for him. * A general implication that he came up with this stuff himself, which he didn't. Arena allocation (under that name) dates back to the sixties.
- GoblinSlayer 4y agoHe talks about gamedev, and arena allocators are common sense in gamedev. It's more like introduction of a gamedev concept to business programming. Also: >Learning how to work with arenas entirely revolutionized my experience with writing code in C. But, right, arena allocator is a tool, not a solution, you still have to invent a solution every time, it's still manual memory management, just an easier one.
- drainyard 4y agoIf it seems like he is implying he came up with arena allocators himself, that is definitely not intended. I didn't read it like that though.
- rfleury 4y ago> A general implication that he came up with this stuff himself, which he didn't. How in the world did you get this from the article? Of course I didn't invent it, nor did I ever claim (or imply) that.
- omnicognate 4y agoI don't think you intended to create that impression, but I didn't go looking for it either. It's created by phrasing like > In this post, I’ll be presenting an alternative to the traditional strategy of manual memory management that I’ve had success with: the arena allocator. and > My approach, on the other hand, is this: instead of assuming that malloc and free were the correct low-level operations, we can change the memory allocation interface... Together with a complete absence of any reference to the history of the technique or any of the people besides you involved in its development. It's not an accusation, but it's a clarification worth making as I genuinely think someone without knowledge of the subject could come away thinking you were presenting your own innovation.
- GoblinSlayer 4y agoArena allocator lets you group allocations greatly reducing difficulty of correct manual memory management. As a bonus icache is used more economically too.
- DougBTX 4y agoThe author should take another look at RIIA. The first big difference is that it works with non-memory resources too, for example open/close for files. The second is that it makes heap allocated data feel a lot like stack allocated data, since the resources are ultimately owned by variables on the stack. With “move semantics” resources can be passed as arguments and returned by functions too, so the example of lifetimes crossing calls is fine. A function could take in a File, read some data, use that to select and open a different File, then return the new file and close the old file at the end of the call, and it would all be straightforward. This is all present in C++. If it seems like an interesting idea to learn more about, I recommend trying it in Rust. It is the default, so there’s minimal syntax for it and all the standard library uses it.
- GoblinSlayer 4y agoIt's a C++ myth that files and memory are the same problem, in reality files are an easier problem, cf java.
- saurik 4y ago?! Java doesn't offer a reasonable solution for files... it is presumed they can be closed by the garbage collector calling "finalizers", but that results in problems such as holding file locks long after they should have been released (which is a correctness problem) and running out of file handles (a limited resource that Java does not track pressure against) due to this mechanism having no time horizon. The result is that you are essentially trapped into the realm of manual resource management, having to put a try block around every single use of a file to make sure you can control its lifetime and prevent it from "leaking" into the collector. You then would have to use a finally block and call close. Memory is certainly the easier problem as it is a single constrained resource with usage semantics (licenses and conflicts) entirely constrained by the language. (They at least have recently added some syntax that makes the last bit of that easier, but which doesn't at all make it less manual. This syntax is somewhat based on C#'s using blocks, a feature which I might have actually caused due to some strong advocacy surrounding this issue when .NET was in beta, and yet I have always insisted this syntax failed to correctly understand the problem as, even if you buy into the manual-ness of it all, it leads to colored objects, as there are objects controlled by the garbage collector and objects controlled by using and now adding a field to an object that must be disposed flips it from one regime into the other regime without causing a type incompatibility.)
- quickthrower2 4y agoIs there an equivalent insight but instead of C it is web dev and instead of Malloc it is JQuery and GC being React? In other words is there an elegant way to build a complex SPA with vanilla JS
- zozbot234 4y ago> In other words is there an elegant way to build a complex SPA with vanilla JS Svelte and SolidJS. They add a compilation step that turns fully abstracted SPA framework code into vanilla JS doing efficient DOM operations. If you wanted something even closer to the speed of C, you could imagine a framework using WASM to generate arbitrarily complex raw HTML and add it to the DOM in a single operation. This would basically match the speed of plain old SSR, with only the limited overhead of running WASM on the client.
- quickthrower2 4y agoThanks! Solid looks quite nice, looking at the docs it is like React but by making state getting a function, and tracking deps automatically, you change the perspective so that you don't need multiple renders (at least in the code you write, not sure about under the hood).
- jart 4y agoAm I the only one who finds it sad when people flagellate themselves before bringing up the topic of C programming?
- Arnavion 4y agoThere's no good replacement for it. For the people using C today, Zig isn't ready for production yet, C++ and Rust are too complicated and don't have the same platform support, and managed languages are obviously out.
- ufo 4y ago> the implementation simply reserves the address range in your virtual address space (e.g. by using VirtualAlloc on Windows). Does anyone know what would be the alternatives to VirtualAlloc on other platforms?
- adrian_b 4y agoOn Linux and other UNIX-like operating systems, that would be mmap with the MAP_ANONYMOUS flag.
- deleted 4y ago[deleted]
- pixelfarmer 4y agoWith Linux+glibc, malloc() makes use of different syscalls. Beside mmap(), there is also brk() in use: https://man7.org/linux/man-pages/man2/sbrk.2.html https://man7.org/linux/man-pages/man2/sbrk.2.html The big question about all that is when this memory is actually being released back to the OS, because by default it isn't really. That also means you can trigger OOM faults despite the process having free memory even (which the OS sees differently).
- adrian_b 4y agobrk/sbrk are obsolete legacy functions. Memory allocated with mmap is released with munmap. brk/sbrk can increase the size of the data segment, thus allocating memory, but they can also decrease the size of the data segment, freeing the memory. Whether calling the function free of the standard C library results sometimes in also invoking munmap or brk/sbrk to release memory to the operating system is obviously implementation dependent. When malloc/free are bypassed and you get memory from the OS directly with mmap, to be used by a custom memory allocator, e.g. an arena allocator, then it is up to you to call munmap when the memory is no longer needed.
- andreareina 4y agoIsn't stack/arena allocation still vulnerable to dangling pointers?
- vitiral 4y agoOf course, it's still manual memory and pointer management after all. You could manually assign a pointer to 0xDEADBEEF if you want, or you could tell the arena to pop data that you are still using. The point is not perfection, it is simplicity and mapping tools to requirements.
- andreareina 4y agoMaybe I'm not understanding the point of the article then. It starts out as a polemical rant against the suggestion to use safer languages, and I guess I expected it to culminate in something more profound wrt safety. I use managed memory languages so I'm sure there's stuff I'm missing.
- williamcotton 4y agoThe point of the article is that there are useful heap memory management techniques that live between the extremes of malloc and free pairs and garbage collection. All of my formal instruction in C never mentioned this middle ground and it took both trial and error and working experience to learn about memory arenas. Also, I’m convinced that evangelizing Rust memory safety has probably done more harm than good because to outsiders it is being made to seem like anything done in C is wrong and stupid and therefore no one should pay any attention to anything that has ever been written in C. One of my worries before I start working in Rust is that I won’t be able to use my knowledge of resource management in C. That’s not the case at all but it takes having to filter through flame wars to figure things out!
- rfleury 4y agoYes, although there are debugging techniques you can use to mitigate the issue. For instance, in debug builds, upon popping off an arena, zero all popped pages, and mark them as no-access.
- SleepyMyroslav 4y agoFor me as non native speaker it is interesting choice of words with 'Untangling'. While arenas achieve the goal to simplify lifetime management by reducing number of different lifetimes they do it by effectively 'entangling' lifetimes. If arenas are reused they make UAF detection a problem that needs special care. Performance is usually great so it is used everywhere not only in gamedev.
- vitiral 4y agoThey are untangled from each other. UAF would be the same difficulty as allocated arrays I would think?
- rfleury 4y agoA fair point on the title! I think you can see it from both perspectives. When I chose the title, I was thinking of "untangling" in this sense: In the traditional C program that overuses malloc/free, you end up in a forest of dynamic lifetimes all "tangled together". So, arenas are useful in "untangling those", in the same way that you might untangle cables for your PC by bundling them together.
- sfpotter 4y agoThis article is about 10 times longer than it needs to be. Also no need for the dripping condescension. I’m even among the most receptive to what he’s trying to say, but got exhausted after about 20% of the article and had to give up.
- vitiral 4y agoAs someone already sold on arenas I appreciated both the depth and battle story style. Perhaps the author is mis-targeting: they are preaching to the choir instead of converting the sinners =]
- sfpotter 4y agoIf they’re preaching, it’s quite a long and boring sermon, IMO ;-) Battle stories should be exciting
- Diggsey 4y agoYeah, arena allocators are a great tool, but they are not a magic bullet either. Also the author's condescension about automatic memory management is... telling. Remembering to call "free" is only one small part of why tools like RAII are good, and arena allocators do not help with the other parts. The overall goal is to be able to write correct, reliable, performant software. Arena allocators help prevent missed or double frees, but they don't help with the other memory safety issues. For example, sometimes objects in different arenas need to reference each other and C does not help prevent you from accessing those references after one of the arenas has been freed. On top of that there are general issues with arena allocators: 1. They can be inefficient for resizable collections where you don't know the total length in advance. The multiple re-allocations results in a lot of wasted space in the arena. 2. In C, it's implicit which objects should belong to which arena. 3. It's not composable. A library doesn't necessarily know which objects should use which arena, or may not even support arena allocation at all. 4. There are other resources than just memory. There's a reason it's called RAII and not MAII - because it allows you to clean up all kinds of resources (eg. file handles) when an arena allocator doesn't typically support any kind of destructor. 5. It requires more thought to structure your program in this way. For some programs that may be effort well spent, but a lot of programs are not bound by allocation performance, and for those programs not thinking about allocation at all leaves more time for thinking about correctness in other aspects. 6. Languages which do automatic memory management can still support arenas, and may offer additional benefits when they do (eg. Rust's explicit lifetimes can tie an object to the arena it came from). 7. The stack and heap are global resources that most code can simply assume exist, and use with no extra ceremony. When you have arenas in play, these need to be passed as additional arguments. Adding a new allocation to a function can require sweeping changes to add a new arena parameter to every function above it in the call stack.
- gjulianm 4y ago> In other words, you may treat the operating system as “the ultimate garbage collector”—freeing memory when it is unnecessary will simply waste both your and the user’s time, and lead to code complexity and bugs that would otherwise not exist. Unfortunately, many popular programming education resources teach that cleanup code is always necessary. This is false. I disagree. One, I think the set of C code where both "allocates enough pointers that calling 'free' is too complicated" and "memory leaks are not a problem due to short runtime/low memory usage" is very small. Two, having a pattern where calling free is too complicated might indicate problems with the code: if your code makes it hard to manage lifecycles, I bet it makes several other things harder than they should be. Three, one should think what happens when that code is used in other places: writing proper lifecycle management at the beginning is easier than doing it when it's used as a library. > This simplifies all codepaths in this system. The parsing code becomes simpler, because it does not have to have any cleanup code whatsoever. The calling code becomes simpler, because it does not have to manage the lifetime of the parsed tree independently. And finally, the allocator code itself remains nearly trivial, and lightning fast. Again, disagree. The calling code becomes more complex because now you have to link the arena to the object it creates. It's very easy to make the mistake of passing the object without the associated arena, then releasing the arena, and boom the object is now garbage. And, as I saw in another comment, cleanup might involve more things than just "freeing memory", so you might end up iterating all the structures nevertheless. On the other hand, with RAII and smart pointers (or GC, or refcounting), you don't have to manually iterate through the structure and free and do cleanup. When things go out of scope, they get cleaned up and deallocated, and that's it. But the main issue is memory fragmentation and overallocation. Precisely the example of a JSON tree, where possibly multiple conversions are done and auxiliary structures are allocated, is where I think not all things will be allocated in a perfectly linear fashion, and therefore freeing in a stack allocator will lead to a lot of unreachable memory inside of the stack. I find it surprising that a post that talks about arena allocators doesn't even mention "fragmentation". Are arenas a good tool? Yes, and like every good tool, they have advantages and pitfalls. This post makes it look like they're better than RAII and garbage collectors, and that's false. And the main problem isn't that it does so by virtue of having different opinions, but by hiding the pitfalls of arena allocators.
- 4y ago
- cryptonector 4y agoMy take is that arenas are very useful, but not to use as a stack so much as to allocate a bunch of memory piecemeal but free it all at once. (The "pop" function in TFA is just not relevant to my use cases for arenas.) For example, if you're decoding a certificate, or maybe something larger and more complex, a decoder might malloc() every little thing as it goes, which then necessitates free()ing each of those things when you are done with the whole decoded thing. But if you can have the decoder allocate from an arena, then when you're done using the decoded object you can just free the arena. The decoding example is very common. Whether it's JSON, XML, ASN.1/DER/whatever, Protocol Buffers, Flat Buffers, or anything else, it is very common for decoders to create a ton of garbage to collect. Optimizing that garbage collection seems like a useful thing to do, but it's hard to do in a memory-safe language because every reference to a sub-object of the decoded object will need to be dropped in order for the object's arena to be released. How would one handle this in Rust, C++, or Java?
- cma 4y agoFor c++ you can require the destructor be removed with ~Foo() = delete; Or be trivial (https://en.cppreference.com/w/cpp/language/destructor#Trivial_destructor https://en.cppreference.com/w/cpp/language/destructor#Trivia...), then use compile time checks on std::is_trivially_destructible || !std::is_destructible to be allowed into the arena.
- pencilguin 4y agoIt is odd to present arena allocation as a technique for C when it is most conveniently used in C++. C++'s Standard library has numerous accommodations to this method, and the core language definition acknowledges as legitimate constructing new objects over top of undestructed old objects. It has been used as long as C++ existed. Code using it is clean and maintainable. I gather Rust is beginning to accumulate similar accommodations. It is already explicitly "safe" to seem to leak memory.
- jeltz 4y agoWhile it might be more conveniently used in C++ the use of arena allocators in C is ancient and it can be pretty convenient even in C. The PostgreSQL code base for example makes heavy use of arena allocators.
- feoren 4y ago> But don’t worry, kiddo; in next class, you can return to your “safe” and “managed” padded-room languages where bugs and instabilities are “impossible” (or so they claim). Nobody has ever claimed this, ever, making this a major strawman. Does the author also consider everyone a childish padded-room pussy if they like seatbelts in cars and safety-related infrastructure on highways? The intro to an article should be to engage the reader and get them invested in your topic. Straw-manning memory-managed languages as something designed only for weak-minded children does quite the opposite of this. > an interface and its implementation are intrinsically related in subtle ways. Yes, they are linked in that the implementation is constrained by its interface. > when the nature of the implementation must change, the interface must also fundamentally change The author wildly misunderstands interfaces and abstraction here. Interfaces are not for the implementer! They are entirely for the consumer! Interfaces change when the requirements of the consumers of that interface change, not when its implementations change. Although honestly I have no idea what this has to do with his main point: malloc/free aren't changing, so ... ? > Another attempted solution is garbage collection, which is a large enforcement structure that tracks everything and interrupts productive work in order to perform its function (much like a government agency, except in this case, the garbage collector is ostensibly doing something approximating useful work—although both function by stealing valuable resources involuntarily). Tip for the author: if you're trying to convince me of something, you may want to avoid sending out "I am an narrow-minded asshole who understands nothing and is angry about everything" signals. It makes me think whatever you're trying to convince me of is only for angry, narrow-minded assholes who understand nothing and are angry about everything. > modern programming thinking (and education) ... claims many problems are gross and complex, and thus we need abstraction to make them appear simpler. But not our Great Prophet Author. He knows the world is a Simple Place where Government Bad and Garbage Collection Bad and Universities Bad and Everyone Else Stupid and Everything Is Easy If You're Not An Idiot and Nothing Changes and Users Are Stupid and Programmers Are Stupid and Educators Are Stupid and everyone's just making everything way too hard and if only people would listen to his rants then they'd stop being so stupid. (And fundamentally misunderstanding abstraction, again). I really want to know what Arena Allocators are. I've never heard of them. They sound cool. But this is one of the most arrogant, narrow-minded, condescending, ignorant authors I've seen posted on Hacker News. I guess I'll read about it elsewhere. And this angry asshole is asking for subscriptions!?