Live data from Hacker News

Untangling Lifetimes: The Arena Allocator

rfleury.com

51–60 of 94 posts

Re: Untangling Lifetimes: The Arena Allocator

#51

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

I'm not saying that arena allocators are a bad tool. It seems like a very good fit for your case, where the lifetime of a request is pretty well constrained, and issues like fragmentation and overallocation won't matter too much. But still I think the OP completely misses the discussions of these aspects.

Re: Untangling Lifetimes: The Arena Allocator

#52
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 just uses c++ constructors, destructors and lifetime rules to automatically do one thing when an object is constructed on the stack, and automatically do another thing when control leaves the scope in which the object exists. The second part (the destructor) is key, because it will be executed whether control leaves 'normally' or because an exception was thrown.

Re: Untangling Lifetimes: The Arena Allocator

#53

Earlier quoted context omitted.

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.

Ok "flat-copy-with-src-invalidation". Somehow "move" makes it sound more efficient than copying a C struct, but it's actually the same thing (for POD structs), for non-POD it's even slightly more expensive because you need to fix up the source.

Re: Untangling Lifetimes: The Arena Allocator

#54
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 is awfully misnamed.

It's not about initialization. It's about cleanup. The initialization aspect of RAII is incidental to its main purpose/use -- perhaps essential to it too, but not so essential that initialization becomes more the essence of RAII than cleanup.

It's really a C++ specific form of Lisp's unwind-protect.

Re: Untangling Lifetimes: The Arena Allocator

#55
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…

> If I remember correctly, it effectively states that variable declarations amount to reserving memory resources and nothing else; memory is not zeroed, for instance.

Not really. The idea is that any resource lifecycle management should be tied to the lifecycle of an object, therefore removing a whole class of problems. For example, when you open a file handle, the "open" operation should be tied to a class constructor, and the corresponding cleanup to the destructor. If you lock a mutex, that operation should be handled by a constructor, and the release by a destructor, so that you never forget to do the cleanup.

> My question is: what makes this so useful? Looking at wikipedia, the focus seems to be on lifetime guarantees, but I'm having trouble understanding how these are useful in practice.

I think Wikipedia should have an example of the things that RAII makes you avoid. Compare these two designs

    class Config {
        Config() { this->file = nullptr; }
        void load(const std::string& path) { this->file = open(path); }
        void close() { this->file->close(); }
versus

    class Config { 
        Config(const std::string& path) { this->file = open(path); }
        ~Config() { this->file->close(); }
The first design makes it easy to forget calling close() before releasing the object, or maybe writing a destructor that does a double free if close() had already been called... The second design is clear, and also safe to use around exceptions.

As for the comparison with another language... I don't know enough about Go, but RAII is present in a lot of object oriented languages. For example, in Python, opening a file creates a "file" object with the resource (the file handle) already allocated, and when that object refcount goes to zero, the underlying handle is released. However, because in modern languages all the "resource management" is usually done in standard libraries and memory allocation isn't done manually, programmers of those won't really hear about RAII, because all that is managed already in the standard libraries.

Re: Untangling Lifetimes: The Arena Allocator

#56

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.

cf Java? Both in it and c# forgetting to close handles has been one of the most common mistakes I've seen. It pretty much never happens on c++.

I saw it two times in real world C++ code and never saw it in C# and java. Memory leaks are much more common.

Re: Untangling Lifetimes: The Arena Allocator

#57

Earlier quoted context omitted.

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

Ok "flat-copy-with-src-invalidation". Somehow "move" makes it sound more efficient than copying a C struct, but it's actually the same thing (for POD structs), for non-POD it's even slightly more expensive because you need to fix up the source.

I'm definitely not helping this conversation but:

> "flat-copy-with-src-invalidation".

That's not what a move is in C++, at all. I wish we got destructive moves in C++, but we didn't (and I understand why..)

> for non-POD it's even slightly more expensive because you need to fix up the source.

This is just false.

Re: Untangling Lifetimes: The Arena Allocator

#58
post #14

> 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?

On Linux and other UNIX-like operating systems, that would be mmap with the MAP_ANONYMOUS flag.

With 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

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

Re: Untangling Lifetimes: The Arena Allocator

#59
post #36

Earlier quoted context omitted.

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.

The 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!

Re: Untangling Lifetimes: The Arena Allocator

#60

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.

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

That's the myth, C++ way is not the only true way. Many C++ programmers know only C++ way and can't imagine anything else, because they spent all their life on learning C++ and it consumed all their mental resources.
Post reply on HN