Live data from Hacker News

Static Allocation with Zig

nickmonad.blog

51–60 of 112 posts

Re: Static Allocation with Zig

#51
post #3

Earlier quoted context omitted.

Yep. Those of us who write embedded code for a living call this “writing code.”

Author here! That's totally fair. I did learn this is a common technique in the embedded world and I had a whole section in the original draft about how it's not a super well-known technique in the typical "backend web server" world, but I wanted to keep the length of the post down so I cut that out. I think there's a lot we can learn from embedded code, especially around performance.

Back in 2005 Virgil I, which target MCUs like AVR, had static initialization and would generate a C program with all of the heap statically allocated, which was then compiled into the binary. C programmers for AVR are used to just declaring globals, but Virgil allowed arbitrary code to run which just initialized a heap.

Virgil II and III inherited this. It's a standard part of a Virgil program that its components and top-level initializers run at compile time, and the resulting heap is then optimized and serialized into the binary. It doesn't require passing allocators around, it's just part of how the language works.

Re: Static Allocation with Zig

#52
post #27

Earlier quoted context omitted.

If you use it and stop using it, the OS cannot reclaim the pages, because it doesn't know that you've stopped. At best, it can offload the memory to disk, but this waste disk space, and also time for pointless writes.

This is true, whether it matters is context dependent. In an embedded program, this may be irrelevant since your program is the only thing running so there is no resource contention or need to swap. In multi-tenant, you could use arenas in an identical way as single static allocation and release the arena upon completion. I agree that allocating a huge amount of memory for a long running program on a multi-tenant os…

Yes it is context dependent, but the parent comment was acting as if it was just better. I wanted to correct that.

Re: Static Allocation with Zig

#53
Didn’t we solve this already with slab allocators in memcached? The major problem with fixed allocation like this is fragmentation in memory over time, which you then have to reinvent GC for.

Re: Static Allocation with Zig

#54
In a strange coincidence (or maybe its actually inevitable given the timing) I also saw a podcast with Matklad of Tigerbeetle and had a similar idea--I've been working on a massively multiplayer game as a hobby project, also built in zig, also fully allocating all memory at startup, and also had an experience almost identical to OP's. In my case both my client and server are in Zig. Zig is pretty great at doing performant game code (rendering and physics on the client) ... it's less great on the server compared to Go (early days and fewer batteries included, fewer things just work out of the box ... but you can find pretty much everything you need for a game server with a little hunting and pecking and a debugging few build issues).

Zig also works "okay" with vibe coding. Golang works much better (maybe a function of the models I use (primarily through Cursor) or maybe its that there's less Zig code out in the wild for models to scrape and learn from, or maybe idiomatic Zig just isn't a thing yet the way it is with Go. Not quite sure.

Re: Static Allocation with Zig

#55

> All memory must be statically allocated at startup. No memory may be dynamically allocated (or freed and reallocated) after initialization. This avoids unpredictable behavior that can significantly affect performance, and avoids use-after-free. As a second-order effect, it is our experience that this also makes for more efficient, simpler designs that are more performant and easier to maintain and reason about, com…

> a technique known for 30+ years in the industry have been Knowledge sharing with next generations is one of those very tricky things. For one thing, how would I know where to find this? What book? What teacher? There are so many books, must I read all of them? What if my coworkers awaren't aware of it, how can they share it with me? Also, an old saying goes, if you're good at something, never do it for free. This i…

Trade secrets are not necessary to build up interest/visibility momentum. And likely "wasted" for external rewards, without pre-existing momentum.

(External = a marketing/sales bump. vs. Internal = enjoy sharing, writing to self-clarify, writing practice.)

Developing the abilities to repeatedly pick attractive topics (not as widely known as they are useful/interesting), and communicating (in a clear well-received voice/style), until those are validated by increasing visibility and sales/marketing impact, is where to start.

After that, the choice to share a trade secret can be made based on a more predictable reward trade off.

But every post already made, is a post to build on.

Re: Static Allocation with Zig

#56

One key thing to understand about TigerBeetle is that it's a file-system-backed database. Static allocation means they limit the number of resources in memory at once (number of connections, number of records that can be returned from a single query, etc). One of the points is that these things are limited in practice anyways (MySQL and Postgres have a simultaneous connection limit, applications should implement pagi…

Yes, very good point, thanks!

As a tiny nit, TigerBeetle isn't _file system_ backed database, we intentionally limit ourselves to a single "file", and can work with a raw block device or partition, without file system involvement.

Re: Static Allocation with Zig

#57

> All memory must be statically allocated at startup. No memory may be dynamically allocated (or freed and reallocated) after initialization. This avoids unpredictable behavior that can significantly affect performance, and avoids use-after-free. As a second-order effect, it is our experience that this also makes for more efficient, simpler designs that are more performant and easier to maintain and reason about, com…

To add more context, TigerStyle is quite a bit more than just static allocation, and it indeed explicitly attributes earlier work:

> NASA's Power of Ten — Rules for Developing Safety Critical Code will change the way you code forever. To expand:

* https://github.com/tigerbeetle/tigerbeetle/blob/main/docs/TI...

* https://spinroot.com/gerard/pdf/P10.pdf

Re: Static Allocation with Zig

#58
post #14

> All memory must be statically allocated at startup. But why? If you do that you are just taking memory away from other processes. Is there any significant speed improvement over just dynamic allocation?

1. On modern OSes, you probably aren't "taking it away from other processes" until you actually use it. Statically allocated but untouched memory is probably just an entry in a page table somewhere. 2. Speed improvement? No. The improvement is in your ability to reason about memory usage, and about time usage. Dynamic allocations add a very much non-deterministic amount of time to whatever you're doing.

In response to (1) - you’re right, but that also implies that the added safety from static allocation when running on a modern OS is just an illusion: the OS may be unable to supply a fresh page from your ‘statically allocated’ memory when you actually write to it and it has to be backed by something real. The real stuff may have run out.

Re: Static Allocation with Zig

#59
post #14

> All memory must be statically allocated at startup. But why? If you do that you are just taking memory away from other processes. Is there any significant speed improvement over just dynamic allocation?

See https://github.com/tigerbeetle/tigerbeetle/blob/main/docs/TI... for motivation.

- Operational predictability --- latencies stay put, the risk of threshing is reduced (_other_ applications on the box can still misbehave, but you are probably using a dedicated box for a key database)

- Forcing function to avoid use-after-free. Zig doesn't have a borrow checker, so you need something else in its place. Static allocation is a large part of TigerBeetle's something else.

- Forcing function to ensure existence of application-level limits. This is tricky to explain, but static allocation is a _consequence_ of everything else being limited. And having everything limited helps ensure smooth operations when the load approaches deployment limit.

- Code simplification. Surprisingly, static allocation is just easier than dynamic. It has the same "anti-soup-of-pointers" property as Rust's borrow checker.

Re: Static Allocation with Zig

#60

> All memory must be statically allocated at startup. No memory may be dynamically allocated (or freed and reallocated) after initialization. This avoids unpredictable behavior that can significantly affect performance, and avoids use-after-free. As a second-order effect, it is our experience that this also makes for more efficient, simpler designs that are more performant and easier to maintain and reason about, com…

> All memory must be statically allocated at startup. No memory may be dynamically allocated (or freed and reallocated) after initialization ... It's baffling that a technique known for 30+ years in the industry have been repackaged

This is also how GPU shader programming works: there's no real equivalent to heap allocation or general pointers, you're expected to work as far as possible with local memory and sometimes preallocated shared buffers. So the technique may be quite relevant in the present day, even though it has a rather extensive history of its own.

Post reply on HN