A real-time particle benchmark that compares Go's garbage collector against manual memory management via mmm arenas.
Press TAB to switch between GC mode (200 000 individually heap-allocated particles) and Arena mode (one contiguous allocation, zero GC pressure). Watch the frame-time graph and heap counters to see the difference live.
| Key | Action |
|---|---|
| TAB | Toggle GC / Arena mode |
| SPACE | Toggle auto-spawn (center of screen) |
| UP / DOWN | Increase / decrease spawn rate |
| Left click | Spawn particles at cursor |
go run github.com/gabstv/oh-my-particles@latestgit clone https://github.com/gabstv/oh-my-particles.git
cd oh-my-particles
go run .- Go 1.25+
- A C compiler is not required - raylib-go uses purego (no CGo)
- macOS, Linux, or Windows
In a production game engine, particles are almost always stored in a fixed-size, pre-allocated array (often called an object pool). Dead particles are recycled in place - no allocation or deallocation happens at runtime, so the GC never has anything to collect. This is the standard approach and it works well.
This demo intentionally does not do that in GC mode. Instead, every particle is individually heap-allocated with new(Particle), and dead particles are left for the garbage collector to sweep. This creates a worst-case scenario that maximizes GC pressure: hundreds of thousands of tiny, short-lived objects churning every frame.
The point is not to show that Go's GC is slow - it is remarkably fast for a tracing collector. The point is to show that any tracing GC introduces non-deterministic pauses that are observable in latency-sensitive workloads like real-time rendering, audio, and physics simulation. Even sub-millisecond GC pauses cause visible frame-time spikes when your budget is 16 ms.
Arena mode demonstrates the alternative: a single contiguous allocation that the GC never touches. No pauses, no heap growth, deterministic frame times. The mmm library brings this pattern to Go without leaving the language.