logoalt Hacker News

bilbo-b-bagginstoday at 1:09 AM7 repliesview on HN

Man this person is mediocre at best. You can do fully manual memory management in Go if you want. The runtime is full of tons of examples where they have 0-alloc, Pools, ring buffers, Assembly, and tons of other tricks.

If you really want an arena like behavior you could allocate a byte slice and use unsafe to cast it to literally any type.

But like… the write up completely missed that manual memory management exists, and Golang considers it “unsafe” and that’s a design principle of the language.

You could argue that C++ RAII overhead is “bounded performance” compared to C. Or that C’s stack frames are “bounded performance” compared to a full in-register assembly implementation of a hot loop.

But that’s bloody stupid. Just use the right tool for the job and know where the tradeoffs are, because there’s always something. The tradeoff boundary for an individual project or person is just arbitrary.


Replies

stingraycharlestoday at 8:00 AM

As someone who writes Go code that processes around 100B messages per day (which all need to be parsed and transformed), I can confirm that the author’s position is very much misguided.

And it also completely ignores the fascinating world of “GC-free Java”, which more than a few of the clients I work with use: Java with garbage collection entirely disabled. It’s used in finance a lot.

Is it pretty? No.

Is it effective? Yes.

Regarding Go’s memory arenas, do you need to use memory arenas everywhere ? Absolutely not. Most high performance code has a hot part that’s centered (like the tokenizer example that OP used). You just make that part reuse memory instead of alloc / dealloc and that’s it.

show 1 reply
swidtoday at 1:15 AM

This isn't true in practice because you won't be able to control where allocations are made in the dependencies you use, including inside the Go standard library itself. You could rewrite/fork that code, but then you lose access to the Go ecosystem.

The big miss of the OP is that it ignores the Go region proposal, which is using lessons learned from this project to solve the issue in a more tractable way. So while Arenas won't be shipped as they were originally envisioned, it isn't to say no progress is being made.

sheepscreektoday at 2:01 AM

I personally loved using Go 8 years ago. When I built a proof of concept for a new project in both Go and Rust, it became clear that Rust would provide the semantics I’m looking for out of the box. Less fighting with the garbage collector or rolling out my own memory management solution.

If I’m doing that with a lot of ugly code - I might as well use idiomatic Zig with arenas. This is exactly the point the author tried to make.

Your last paragraph captures the tension perfectly. Go just isn’t the tool we thought for some jobs, and maybe that’s okay. If you’re going to count nanoseconds or measure total allocations, it’s better to stick to a non-GC language. Or a third option can be to write your hot loops in one such language; and continue using Go for everything else. Problem solved.

cafxxtoday at 3:05 AM

> If you really want an arena like behavior you could allocate a byte slice and use unsafe to cast it to literally any type.

A word of caution. If you do this and then you store pointers into that slice, the GC will likely not see them (as if you were just storing them as `uintptr`s)

show 1 reply
0xjnmltoday at 12:18 PM

> If you really want an arena like behavior you could allocate a byte slice and use unsafe to cast it to literally any type.

Only if the type is not a pointer per se or does not contain any inner pointers.

Otherwise the garbage collector will bite you hard.

show 1 reply
bborudtoday at 10:32 AM

Do you have some tips for blog postings, code, articles that explore these topics in Go?

raggitoday at 5:15 AM

> You can do fully manual memory management in Go if you want. The runtime is full of tons of examples where they have 0-alloc, Pools, ring buffers, Assembly, and tons of other tricks.

The runtime only exposes a small subset of what it uses internally and there's no stable ABI for runtime internals. If you're lucky to get big enough and have friends they might not break you, some internal linkage is being preserved, but in the general case for a general user, nope. Updates might make your code untenable.

> If you really want an arena like behavior you could allocate a byte slice and use unsafe to cast it to literally any type.

AIUI the prior proposals still provided automated lifetime management, though that's related to various of the standing concerns, so you can't match that from "userspace" of go, finalizers don't get executed on a deterministic schedule. Put simply: that's not the same thing.

As someone else points out this is also much more fraught with error than just typing what you described. On top of the GC issue pointed out already, you'll also hit memory model considerations if you're doing any concurrency, which if you actually needed to do this surely you are. Once you're doing that you'll run into the issue, if you're trying to compete with systems languages, that Go only provides a subset of the platform available memory model, in the simplest form it only offers acq/rel atomic semantics. It also doesn't expose any notion of what thread you're running on (which can change arbitrarily) or even which goroutine you're running on. This limits your design space quite significantly at the bounds your performance for high frequency small region operations. I'd actually hazard an educated guess that an arena written as you casually suggest would perform extremely poorly at any meaningful scale (lets say >=32 cores, still fairly modest).

> You could argue that C++ RAII overhead is “bounded performance” compared to C. Or that C’s stack frames are “bounded performance” compared to a full in-register assembly implementation of a hot loop. > But that’s bloody stupid. Just use the right tool for the job and know where the tradeoffs are, because there’s always something. The tradeoff boundary for an individual project or person is just arbitrary.

Sure, reducto ad absurdum, though I typically would optimize against the (systems language) compiler long before I drop to assembly, it's 2025 systems compilers are great and have many optimizations, intrinsics and hints.

> Man this person is mediocre at best.

Harsh, I think the author is fine really. I think their most significant error isn't in missing or not discussing difficult other things they could do with Go, it's seemingly being under the misconception prior to the Arena proposal that Go actually cedes control for lower level optimization. It doesn't, and it never has, and it likely never will (it will gain other semi-generalized internal optimizations over time, lots of work goes into that).

In some cases you can hack some in on your own, but Go is not well placed as a "systems language" if you mean by that something like "competitive efficiency at upper or lower bound scale tasks", it is much better placed as a framework for writing general purpose servers at middle scales. It's best placed on systems that don't have batteries, and that have plenty of ram. It'll provide you with a decent opportunity to scale up and then out in that space as long as you pay attention to how you're doing along the way. It'll hurt if you need to target state of the art efficiency at extreme ends, and very likely block you wholesale.

I'm glad Go folks are still working on ideas to try to find a way for applications to get some more control over allocations. I'm also not expecting a solution that solves my deepest challenges anytime soon though. I think they'll maybe solve some server cases first, and that's probably good, that's Go's golden market.