Skip to content

Comment on Game Development in Goparent

Comments

Minecraft seems to be doing okay. The key is to have many small GC pauses instead of few big pauses.

Go is also completely overhauling their GC for the next release (1.5). See http://llvm.cc/t/go-1-4-garbage-collection-plan-and-roadmap-...

Minecraft's also had a ton of time invested into racing the beam with regards to the JVM GC. Not disagreeing with the viability of it, I personally use the CLR because I'm comfortable with that tradeoff and doing my work there too, but it is worth noting that a sufficiently complicated game will spend a lot of time dealing with memory issues.

I'd use either the JVM or the CLR long before Go, though.

I think it's worth it to note that "a sufficiently complicated game will spend a lot of time dealing with memory issues" applies to all games. The memory issues might just be different. Or they could be simpler. Most games (especially large ones) tend to end up with multiple ways of garbage collecting eventually, even if written in pure C++. And that isn't even taking into account cache coherency, NULL pointers, double-freed pointers, etc. At least with something like JVM or CLR, you only have to fight the GC. Whether that's good or bad, that's left up to the developer fighting whatever memory issue is happening at the time.

It's bad.

The reason is because you don't control the GC and don't even necessarily know what exactly drives the decisions it makes. So once you want to go beyond a certain level of performance, there is no right answer. You are just randomly trying stuff and kind of flailing.

In C++ (or another direct-memory language), there is a right answer. You can always make the memory do exactly what you want it to, and there's always a clear path to get there from wherever you are.

> The reason is because you don't control the GC and don't even necessarily know what exactly drives the decisions it makes.

I appreciate the flexibility and choice that a direct-memory language provides, but I think "randomly trying stuff and kind of flailing" is over-the-top. On the JVM you can control the GC quite effectively, with an understanding of the JMM and some experience its behaviors become largely predictable, and profile-directed memory optimization can be tedious, but certainly isn't random. Most Java developers I know are sometimes surprised by the JVM's behaviors...but then, most Java developers I know aren't terribly interested in how the JVM works.

(My professional, non-game work is historically mainly on the JVM. I use the CLR for my game projects because even mobile platforms have an embarrassing surplus of performance relative to my needs and it's a lot more cross-platform than the JVM. I'm comfortable enough in C++, but I'm much slower at working with it--and I'm slow enough that I need all the help I can get!)

Thanks for taking the time to comment, Jonathan.

This is why the approach I'm experimenting with is build something very much like a custom allocator in Go, for all values that are allocated in significant numbers. I'm hoping that this will take enough pressure off the GC that it will keep pauses below the threshold where they matter (see above for a caveat about needing a concurrent or incremental GC to avoid long, but less frequent pauses). For what it's worth, I'm not 100% certain that this approach will work well enough, but I'm hoping to get some data that we can use to debate this in more concrete terms.

If this does work well, awesome. If not... well, I'm still tinkering with Rust, but I found the type-parameter explosion off-putting enough that I decided to stick with Go for my first round of experiments. I'm curious how your experience with more limited (as I understand it, perhaps incorrectly) allocation annotations are working out in Jai. After all, I'm not dead set on using Go -- I just want to avoid writing C++ for hobby games if I can possibly avoid it :)

You can always make the memory do exactly what you want it to, and there's always a clear path to get there from wherever you are.

Only if you write your own memory allocator, otherwise relying on the compiler provided allocator is no different.

Which is why people who are serious about memory write their own allocators (or link preferred allocators with known behavior). It is an extremely common thing.

Sure, I wasn't disagreeing with you per se, as I am well aware of your nick.

Just mentioning the issue for other readers, as many think malloc/NEW/Allocate or whatever is called, is fast.

JVM or the CLR long before Go, though.

Possibly only because they have been around for longer. The CLR 1.0 GC was a terrible beast. I'm sure that the earlier Java GCs were horrible things, too.

sufficiently complicated game will spend a lot of time dealing with memory issues.

This is precisely why gamedevs are going for data oriented design, it all does come down to this at the end of the day. In theory a GC doesn't actually get in the way of DOD, because in the strictest definition it simulates infinite memory (it is, strictly, not a memory reclaiming device). GCs are getting better and better at doing this with less and less overhead. The newest concurrent CLR GC is pretty impressive, it very nearly never has to stop-the-world.

Sorry, I didn't mean to imply that I'd use them because of Go's garbage collector, which is vastly improved and arguably the best part of the entire ecosystem these days. I'd use the JVM or the CLR mostly because I am more convinced than I am about almost any technical topic that Go is a creeping, faddish horror that resists decent design practices for applications over a trivial scope, made by a team that took all the wrong lessons from Java and C++ and made a language worse than one or the other at almost every task that I can think of.

The JVM also has probably the most man-years of effort into GC optimization. One thing the CLR has going for it is value types, which make arrays-of-struct possible (instead of arrays-of-refs-to-objects). I assume Go supports this too.

See my earlier comment (and some bits of the original post). Go does indeed support arrays-of-structs, as well as taking pointers to the middle of arrays, and directly to struct fields. This gives you a lot more control over memory layout, and lets you avoid creating garbage if you're willing to put just a bit more work into it.

AboutSource Built by g1lg1l

Hackerly is an independent reader for Hacker News, built on the public HN API. Not affiliated with Y Combinator.