3 ms·
> I think you're basically set and don't need GC for games. This is kind of a semantics question, I think. The vast majority of games need some sort of lifetim
by kevingadd 5y ago
> I think you're basically set and don't need GC for games.
This is kind of a semantics question, I think. The vast majority of games need some sort of lifetime management, and it's just a question of who does the lifetime management and what mechanism you use for it - refcounting, a mark/sweep GC, freeing everything at certain points of time, arenas, etc. If you're using an entity/component system to manage lifetime you have a GC - you wrote it.
In my experience shipping games in C# compared to shipping them in C/C++ - you can do everything without the GC touching your stuff if you're really dedicated, but it's often not worth the trouble considering that any modern GC can handle scattered temporary per-frame allocations for you no problem with very minimal pause times, as long as you're thoughtful about it and the set of objects it needs to walk isn't too big. For example, if your data structures mix native data with pointers to object instances, a GC will have to sweep all of that data - splitting texture references out of a big table of draw calls means that the draw calls are now pure data and they don't need to be swept by a GC.
Personally I prefer always having access to a GC because it means code that doesn't need careful lifetime management can be simpler to write and doesn't have issues like double-frees hiding inside it - things like automated tests, configuration UIs, debug consoles, and things you run once at startup or when loading a level. You can often go back and optimize some of this stuff later, too - for example LINQ is a notoriously messy feature in .NET's standard library that allocates tons of short-lived garbage, but the compiler makes it possible to replace all those LINQ data structures with non-allocating ones without having to rewrite your queries - but doing that moves costs elsewhere.
If you're getting specifically harassed by pause times you're likely going to be paying costs with other systems, like if you use refcounting any time you touch that refcount you're burning cpu cycles and pushing other stuff out of cache (and the refcounting gets much more expensive if you have to use atomics for thread safety).
- nikki93 5y agoYup, pretty much. I do think that "GC" usually these days refers to a mark and sweep GC (certainly in the context of a discussion about Go?) or at most a refcount kind of thing applied to all reference-like language entities, not "any lifetime management system," the way I see folks use that term. But yeah I've found that the entity component data structure is a good lifetime management system very well suited to the game scenario, so there's no need for a different / more complex thing. And this is an exploration in how that + a simple / ergonomic language around it (along with growable arrays (slices)) pan out when making games in practice. There's no manual free calls or lifetime management anywhere in the code. Re: "using GC but then needing to / being thoughtful to make sure it's going ok" -- that's the thing. It seems better to not have to need to think about it, by having a system that's better suited to the thing you are working on. You also don't need to "often go back and optimize some of this stuff later too" because it just already has good performance with the straightforward code, and you don't need to add complexity. "splitting references out / pure data" -- that is indeed what the language nudges you to do by only having pure data. Essentially: yes, you can achieve the desired thing with intentionality and extra cognition in a different system (the same was true with other kinds of cognitive overhead in C++) and this is an exploration in developing a language + tools that focus on and bias toward the desired thing by default. Like I'm imagining folks getting started with gamedev using this + the integrated tooling and internalizing the practices you're talking about (that's a stretch vision, the current scope is to just build and test it in the context of one specific game project). I'll be releasing this engine + an example game with it soon, but here's what the code for this main game project (the one in the video) looks like (all the components, the top-level game loop, and then some example game logic): https://gist.github.com/nikki93/0425d9ead9eb7810075434d006f3b790?ts=2 https://gist.github.com/nikki93/0425d9ead9eb7810075434d006f3... It's just data structures, and then functions that do gameplay stuff on them. No lifetime management.
- funcDropShadow 5y ago> Yup, pretty much. I do think that "GC" usually these days refers to a mark and sweep GC Mark and Sweep is antiquated for GCs. There are some edge cases where it is still useful. But most heavily used sytems should use more modern GC algorithms, e.g. generational GCs.
- titzer 5y agoI agree completely with this. There's been a widely-held misconception, brought on by certain programming styles and libraries, that GC implies trashy, fat programs that are wasteful. But in fact, as you point out, programs that are tight about memory allocation, but nevertheless GC'd for a few things here and there, can still run great with little overhead. Allocation discipline is more important than most other considerations, IMHO.
- nikki93 5y agoI also tried running vanilla Go in Wasm (one of the targets I want to support) directly and was hitting GC pauses and frame drops every second. Garbage generated due to temporaries has also been an issue in an engine I worked on that had Lua scripting. This isn't just a theoretical thing, it's clear it's a problem. When you say "programs that are tight but" and "allocation discipline" it's like why add the cognitive overhead when you can not have the problem at all. I want to / want people that use this engine to be able to just write naive / straightforward code and get a high perf ceiling off the bat. GC is not actually helping in the game case since game logic is ultimately explicit about entity lifetimes. So there isn't a reason to use it and then work around it. If anything I think it's because the ergonomic language work after C++ (C#, scripting languages) tended to include GC so using them meant having it. This is an exploration in having a language that doesn't do that and preserves ergonomics (and it's working / promising). All that said, the GC thing isn't the main or only reason that motivated this approach, it's just one of the points among everything else. Portability (the resulting C++ compiles and runs in Wasm, native desktop, mobile is supported) and control (being able to decide language and resulting execution semantics, having direct integration with tools) are the main things, at a high-level.