Live data from Hacker News

Untangling Lifetimes: The Arena Allocator

rfleury.com

41–50 of 94 posts

Re: Untangling Lifetimes: The Arena Allocator

#41
post #7

The 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 lifeti…

It's a C++ myth that files and memory are the same problem, in reality files are an easier problem, cf java.

I would argue that the opposite is true, because memory is fungible and files are not.

c.f.) Java, where the GC can handle memory, but it didn’t work out so well for files, so, to restore correctness, we needed the Closeable and AutoCloseable interfaces to give manual control back to the programmers.

Re: Untangling Lifetimes: The Arena Allocator

#42

> 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 "…

I use arenas for HTTP request handlers. The JSON built or parsed is only going to be around for the lifetime of the request. It's much simpler and faster to use an arena than to do reference counting wrapped around malloc/free.

Here's an example where in a commit I swapped in an arena:

https://github.com/williamcotton/express-c/commit/4ae53f38e3...

Re: Untangling Lifetimes: The Arena Allocator

#43
post #26

The author may be interested in Zig, where allocators are first-class components of the system. https://ziglang.org/documentation/0.9.1/#Choosing-an-Allocat... There is even an arena allocator provided (amongst others) by the std lib.

You 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-all...

Re: Untangling Lifetimes: The Arena Allocator

#44
post #7

The 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 lifeti…

Could someone please explain the usefulness of RAII? As I understand it, RAII refers to the initialization semantics of certain languages, most famously C++. If I remember correctly, it effectively states that variable declarations amount to reserving memory resources and nothing else; memory is not zeroed, for instance. My question is: what makes this so useful? Looking at wikipedia, the focus seems to be on lifetim…

Go has defer, so you can write:

   f = os.Open(...)
   defer f.Close()
   // do stuff with f...
This ensures you close the file at the end of the scope. If processing the file is complex, with lots of places where return is called, then there's lots of places where the Close call could actually need happen. Thankfully you just write it once with defer, and you can forget about it.

In languages without defer you need something else. The RIIA approach is to create a file object where the constructor opens the file and the destructor closes the file. The compiler needs to track the file objects scope in order to call the destructor. This acts as a hook to run some code right at the end of the scope, wherever that actually occurs.

Both call close right before returning, but the actual mechanism is different because the languages have different tools.

Re: Untangling Lifetimes: The Arena Allocator

#45
post #36

Isn't stack/arena allocation still vulnerable to dangling pointers?

Of 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.

Maybe 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.

Re: Untangling Lifetimes: The Arena Allocator

#46

Earlier quoted context omitted.

It's a C++ myth that files and memory are the same problem, in reality files are an easier problem, cf java.

I would argue that the opposite is true, because memory is fungible and files are not. c.f.) Java, where the GC can handle memory, but it didn’t work out so well for files, so, to restore correctness, we needed the Closeable and AutoCloseable interfaces to give manual control back to the programmers.

Closing a file doesn't trigger a cascade of closing other files, while freeing memory in RAII does trigger a cascade of more frees.

Re: Untangling Lifetimes: The Arena Allocator

#47
post #7

The 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 lifeti…

Could someone please explain the usefulness of RAII? As I understand it, RAII refers to the initialization semantics of certain languages, most famously C++. If I remember correctly, it effectively states that variable declarations amount to reserving memory resources and nothing else; memory is not zeroed, for instance. My question is: what makes this so useful? Looking at wikipedia, the focus seems to be on lifetim…

"RAII" has some overlapping associations.

Primary purpose: To guarantee the cleanup of resources during stack unwinding, after an exception is thrown, because it's tightly bound to the automatic storage duration mechanism (the static code generated to manage the stack at runtime).

Secondary purposes: 1) To immediately initialize some resource handler where it is declared. 2) To automate the cleanup of some resource when execution leaves the scope in which it was declared, also hiding explicit calls to Close, Free, Unlock, Release, etc.

To restate the source, RAII was the premise for exceptions. [1]

[1] C++ in Constrained Environments - Bjarne Stroustrup - CppCon 2022

https://www.youtube.com/watch?v=2BuJjaGuInI&t=4000s

Re: Untangling Lifetimes: The Arena Allocator

#48
post #7

The 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 lifeti…

IMHO the problem with C++ style RIIA is that it's not just about calling cleanup code at the end of a scope (this would be fine, really), but the 'rat's tail' that's attached to RAII for dynamic memory management: you also need the compiler to be able to invoke user-provided code for 'copy' and 'move' operations (or better named: deep- and flat-copy), you need different reference types to define ownership details (e.…

> (or better named: deep- and flat-copy)

That's worse naming, not better!

In particular, a copy does not change the thing it copies - but a C++ move does change the thing it moves from.

Re: Untangling Lifetimes: The Arena Allocator

#49
post #7

The 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 lifeti…

It's a C++ myth that files and memory are the same problem, in reality files are an easier problem, cf java.

No, C++ has the idea that the same solution can be used for all resource management: file handles, memory, sockets, database cursors or connections, you name it.

And it works.

Resource life management is not a problem that Java handles with grace...

Re: Untangling Lifetimes: The Arena Allocator

#50

This 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.

Yeah, 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.

Post reply on HN