> Many developers opposed to garbage collection are building “soft” real-time systems. They want to go as fast as possible—more FPS in my video game! Better compression in my streaming codec! But they don’t have hard latency requirements. Nothing will break and nobody will die if the system occasionally takes an extra millisecond.
This depends on the amount of latency. If it’s only “an extra millisecond” than yeah, not an issue. But if garbage collection can take more than entire game tick, than it’s a no-go.
I wrote a small script that simulates a video game. A game scene contains around ten thousand objects, and on every update it creates some new objects and destroys some old ones. It runs for 36,000 iterations (simulating 10 minutes of exciting 60 FPS gameplay) and measures minimum, mean and maximum time per update. One version uses GC, the other uses malloc
/free
.
No-GC version completed the task in 15 seconds, giving a mean update time of 0.4 ms. Maximum was 1 ms.
Naïve GC version took 6 minutes, 40 secs; mean update time was 11 ms. Maximum was 34 milliseconds. That’s two entire updates in a game that runs at 60 updates per second. At some points the game cannot maintain 60 updates per second, dropping to up to 45. Not frames—I’m not rendering anything. Updates. The whole game just slows down.
Profiler reported around 12k collections. Collections could run multiple times during a single update, killing performance. Disabling GC for the duration of game update and calling GC.collect
manually was a big improvement. Simulation took 1 minute 37 seconds, mean update time is therefore 2.7 ms. Maximum is 27 ms, which means occasional framerate drops. Well, at least the game can run at full speed...
So the GC is causing significantly worse performance and worse user experience than simple malloc
. I don’t even have to use object pools or free lists or whatever. Do people who tell us that it’s fine to use use stop-the-world GC in “soft” real-time applications actually run some tests?