Live data from Hacker News

Static Allocation with Zig

nickmonad.blog

101–110 of 112 posts

Re: Static Allocation with Zig

#101
post #90

This might be a silly thing to point out, but where do people draw the line between an allocation happening or not happening? You still need to track vacant/occupied memory even when there's no OS or other programs around. It's especially bewildering when people claim that some database program "doesn't allocate".

This is the fundamental question which motivated the post. :)

I think there are a few different ways to approach the answer, and it kind of depends on what you mean by "draw the line between an allocation happening or not happening." At the surface level, Zig makes this relatively easy, since you can grep for all instances of `std.mem.Allocator` and see where those allocations are occurring throughout the codebase. This only gets you so far though, because some of those Allocator instances could be backed by something like a FixedBufferAllocator, which uses already allocated memory either from the stack or the heap. So the usage of the Allocator instance at the interface level doesn't actually tell you "this is for sure allocating memory from the OS." You have to consider it in the larger context of the system.

And yes, we do still need to track vacant/occupied memory, we just do it at the application level. At that level, the OS sees it all as "occupied". For example, in kv, the connection buffer space is marked as vacant/occupied using a memory pool at runtime. But, that pool was allocated from the OS during initialization. As we use the pool we just have to do some very basic bookkeeping using a free-list. That determines if a new connection can actually be accepted or not.

Hopefully that helps. Ultimately, we do allocate, it just happens right away during initialization and that allocated space is reused throughout program execution. But, it doesn't have to be nearly as complicated as "reinventing garbage collection" as I've seen some other comments mention.

Re: Static Allocation with Zig

#102
post #69
post #65

Earlier quoted context omitted.

> Forcing function to avoid use-after-free Doesn't reusing memory effectively allow for use-after-free, only at the progam level (even with a borrow checker)?

There's some reshuffling of bugs for sure, but, from my experience, there's also a very noticeable reduction! It seems there's no law of conservation of bugs. I would say the main effect here is that global allocator often leads to ad-hoc, "shotgun" resource management all other the place, and that's hard to get right in a manually memory managed language. Most Zig code that deals with allocators has resource managem…

Hey matklad! Thanks for hanging out here and commenting on the post. I was hoping you guys would see this and give some feedback based on your work in TigerBeetle.

You mentioned, "E.g., in OP, memory is leaked on allocation failures." - Can you clarify a bit more about what you mean there?

Re: Static Allocation with Zig

#103
post #90

This might be a silly thing to point out, but where do people draw the line between an allocation happening or not happening? You still need to track vacant/occupied memory even when there's no OS or other programs around. It's especially bewildering when people claim that some database program "doesn't allocate".

This is the fundamental question which motivated the post. :) I think there are a few different ways to approach the answer, and it kind of depends on what you mean by "draw the line between an allocation happening or not happening." At the surface level, Zig makes this relatively easy, since you can grep for all instances of `std.mem.Allocator` and see where those allocations are occurring throughout the codebase. T…

[deleted]

Re: Static Allocation with Zig

#104
post #69

Earlier quoted context omitted.

There's some reshuffling of bugs for sure, but, from my experience, there's also a very noticeable reduction! It seems there's no law of conservation of bugs. I would say the main effect here is that global allocator often leads to ad-hoc, "shotgun" resource management all other the place, and that's hard to get right in a manually memory managed language. Most Zig code that deals with allocators has resource managem…

Hey matklad! Thanks for hanging out here and commenting on the post. I was hoping you guys would see this and give some feedback based on your work in TigerBeetle. You mentioned, "E.g., in OP, memory is leaked on allocation failures." - Can you clarify a bit more about what you mean there?

In

    const recv_buffers = try ByteArrayPool.init(gpa, config.connections_max, recv_size);
    const send_buffers = try ByteArrayPool.init(gpa, config.connections_max, send_size);
if the second try throws, than the memory allocation created by the first try is leaked. Possible fixes:

A) clean up individual allocations on failure:

    const recv_buffers = try ByteArrayPool.init(gpa, config.connections_max, recv_size);
    errdefer recv_buffers.deinit(gpa);

    const send_buffers = try ByteArrayPool.init(gpa, config.connections_max, send_size);
    errdefer send_buffers.deinit(gpa);
B) ask the caller to pass in an arena instead of gpa to do bulk cleanup (types & code stays the same, but naming & contract changes):

    const recv_buffers = try ByteArrayPool.init(arena, config.connections_max, recv_size);
    const send_buffers = try ByteArrayPool.init(arena, config.connections_max, send_size);
C) declare OOMs to be fatal errors

    const recv_buffers = ByteArrayPool.init(gpa, config.connections_max, recv_size) catch |err| oom(err);
    const send_buffers = ByteArrayPool.init(gpa, config.connections_max, send_size) catch |err| oom(err);

    fn oom(_: error.OutOfMemory) noreturn { @panic("oom"); }
You might also be interesting in https://matklad.github.io/2025/12/23/static-allocation-compi..., it's essentially a complimentary article to what @MatthiasPortzel says here https://news.ycombinator.com/item?id=46423691

Re: Static Allocation with Zig

#105
Great read! I'm doing something similar with my game engine. I use a FixedBufferAllocator for static allocation and initialize/allocate all my systems and entities with the necessary size at the start. The only exception currently is asset loading because this can be quite dynamic at times.

Re: Static Allocation with Zig

#106

Earlier quoted context omitted.

Hey matklad! Thanks for hanging out here and commenting on the post. I was hoping you guys would see this and give some feedback based on your work in TigerBeetle. You mentioned, "E.g., in OP, memory is leaked on allocation failures." - Can you clarify a bit more about what you mean there?

In const recv_buffers = try ByteArrayPool.init(gpa, config.connections_max, recv_size); const send_buffers = try ByteArrayPool.init(gpa, config.connections_max, send_size); if the second try throws, than the memory allocation created by the first try is leaked. Possible fixes: A) clean up individual allocations on failure: const recv_buffers = try ByteArrayPool.init(gpa, config.connections_max, recv_size); errdefer r…

Gotcha. Thanks for clarifying! I guess I wasn't super concerned about the 'try' failing here since this code is squarely in the initialization path, and I want the OOM to bubble up to main() and crash. Although to be fair, 1. Not a great experience to be given a stack trace, could definitely have a nice message there. And 2. If the ConnectionPool init() is (re)used elsewhere outside this overall initialization path, we could run into that leak.

The allocation failure that could occur at runtime, post-init, would be here: https://github.com/nickmonad/kv/blob/53e953da752c7f49221c9c4... - and the OOM error kicks back an immediate close on the connection to the client.

Re: Static Allocation with Zig

#107

Earlier quoted context omitted.

Snide and condescending (or at best: dismissive) comments like this help no one and can at the extremes stereotype an entire group in a bad light. I think the more constructive reality is discussing why techniques that are common in some industries such as gaming or embedded systems have had difficulty being adopted more broadly, and celebrating that this idea which is good in many contexts is now spreading more broa…

Marketing is the thing that makes uninformed people adopt thing they don't need. I dont think we need marketing, but rather education, which is the actually useful way to spread information. If you think marketing is the way knowledge spreads, you'll end up with millions of dollars in your pocket and the belief that you have money because you're doing good, while the truth is that you have millions because you exploi…

marketing is how ideas spread. And ideas that spread are those that win.

That's why AI-sloppy software would go viral and make loads of money while properly engineered ones die off.

When people need knowledge, they know where to find it. They don't need marketing for that.

Re: Static Allocation with Zig

#108
post #28

Earlier quoted context omitted.

Personally, I see dynamic allocation more and more as a premature optimization and a historical wart. We used to have very little memory, so we developed many tricks to handle it. Now we have all the memory we need, but tricks remained. They are now more harmful than helpful. Interestingly, embedded programming has a reputation for stability and AFAIK game development is also more and more about avoiding dynamic allo…

> AFAIK game development is also more and more about avoiding dynamic allocation. That might have been the case ~30 years ago on platforms like the Gameboy (PC games were already starting to use C++ and higher level frameworks) but certainly not today. Pretty much all modern game engines allocate and deallocate stuff all the time. UE5's core design with its UObject system relies on allocations pretty much everywhere…

Preciselly because C# uses GC is common to just allocate everything in a chunk to not trigger the gc later.

Aka you minimize allocations in gameplay.

Re: Static Allocation with Zig

#109
post #108

Earlier quoted context omitted.

> AFAIK game development is also more and more about avoiding dynamic allocation. That might have been the case ~30 years ago on platforms like the Gameboy (PC games were already starting to use C++ and higher level frameworks) but certainly not today. Pretty much all modern game engines allocate and deallocate stuff all the time. UE5's core design with its UObject system relies on allocations pretty much everywhere…

Preciselly because C# uses GC is common to just allocate everything in a chunk to not trigger the gc later. Aka you minimize allocations in gameplay.

This is far from common in practice and it is only applied sporadically. Something like allocating formatted strings for the HUD is IME much more common (and done in UE5/C++ too, so not even a C# forcing GC excuse).

Re: Static Allocation with Zig

#110

Earlier quoted context omitted.

Marketing is the thing that makes uninformed people adopt thing they don't need. I dont think we need marketing, but rather education, which is the actually useful way to spread information. If you think marketing is the way knowledge spreads, you'll end up with millions of dollars in your pocket and the belief that you have money because you're doing good, while the truth is that you have millions because you exploi…

You complain about the very thing that lead to the experimentation and writing of this article, which is how one gets a real education: "One of those techniques is static memory allocation during initialization. The idea here is that all memory is requested and allocated from the OS at startup, and held until termination. I first heard about this while learning about TigerBeetle, and they reference it explicitly in t…

If you think that publishing a paper is marketing, then we have quite different views.

Incidentally, I was aware of NASA paper before tigerbeetle was a thing. Not because someone marketed their work, but because I did my research over published ones.

Post reply on HN