4 ms·
I used to work on realtime graphics code which was used in flight simulators for the FAA and USAF, then moved into the games industry and led the design of a co
by oppositelock 7y ago
I used to work on realtime graphics code which was used in flight simulators for the FAA and USAF, then moved into the games industry and led the design of a couple of game engines, to give you an idea where I'm coming from. The military grade flight sims actually had contract requirements like, "you may never miss a single frame in a week of usage", etc, so solving this problem was critical.
When working on code such as game engines, it's not simply a matter of how long something takes to do, but also when it happens and how predictable it is. If I knew that GC takes 1ms, I could schedule it just after I call swapBuffers and before I begin processing data for the next frame, which isn't a latency critical portion.
The problem is that GC is unpredictable because the mark and sweep can take a long time, and this will prevent you from meeting your 60fps requirement.
In practice, we hardly ever do any kind of GC for games because we don't really need to. We use block memory allocators like Doug Lea's malloc (dlmalloc) in fixed memory pools if we must, but generally, you keep separate regions for things like object descriptors, textures, vertex arrays, etc. There's a ton of data to manage which can't be interleaved together in a generic allocator, so once you've gone that deep, there's really no point in using system malloc.
Malloc itself isn't a problem either, it's quick. It's adding virtual space via sbrk() and friends which can pause you, so we don't. On consoles, we have a fixed memory footprint, and on a bigger system, we pre-allocate buffers which we will never grow, and that's our entire footprint. We then chunk it up into sections for different purposes, short lived objects, long lived objects, etc. Frequently, we never even deallocate objects one by one. A common tactic is to have a per-frame scratch buffer, and once you're done rendering a frame, you simply consider that memory free - boom, you've just freed thousands of things without doing anything.
There are many things you can do instead of generic GC which are far better for games.
I disagree with the author of the original article about C++. C++ is as complex as you want to make it, you don't have to use all the rope it hands you to hang yourself. However, having the std library with data structures, smart pointers, the ability to do RAII, is invaluable to me. Smart usage of std::ref gets you automatic GC, what you don't have is cycle detection, so you take care never to have cycles by using weak pointers where necessary, and you have all the behavior of auto-GC without stop the world.
- jackjeff 7y ago> C++ is as complex as you want to make it I second that. I always find the attitude of “C++ is bad so I’m going to stick to C” really bizarre. You can use C++ as a better C. - use type inference and references instead of pointers. Writing C style code with these features makes it more readable. - Don’t like OO programming. Stick to struct with all members public. It’s going to be a lot better than doing the same thing in C and you shall not have void* casts. - use exceptions instead of return code for errors, so that your code is not peppered with if statements at every line you call. With an exception you’ll get additional info about crashes in dumps. I wrote for an embedded system where the C++ standard library was not available, in a previous life. I ended up writing my code in C++ and “re-inventing” a couple of useful classes like std::string and std::vector. For the most part my code was very C like...
- yellowapple 7y ago> use exceptions instead of return code for errors I happen to have the exact opposite opinion. Exception handling tends to feel too "magical" (read: non-deterministic, hard to behaviorally predict, etc.) relative to just returning an error code.
- oppositelock 7y agoException handling doesn't work very well. It requires exception frames on the stack, and those go missing sometimes, leading to leaked exceptions and unexpected crashes. For example, if you pass a C++ callback function into a C callback, say in libcurl, the C library doesn't have exception handler frames in the stack, so exceptions are lost. If your C++ code throws from within that C callback, any catch above the C language boundary won't work. This is just one of countless gotchas. Don't use exceptions.
- ncmncm 7y agoDon't pass functions to libcurl that throw. Otherwise, exceptions make code simpler, cleaner, and more reliable. They will never "go missing" unless you do something to make them go missing. Code that does those things is bad code. Don't write bad code. Do use exceptions.