Threading Gridiron Dynasty's 20-Year World Bootstrap
Gridiron Dynasty’s world bootstrap simulates 20 years of player history before a user sees a game — high school classes through college recruiting, draft classes, NFL seasons, free agency, and progression. All serial. On a 2025 machine, full generation was slow enough that I’d started avoiding it during development. That’s when a performance problem stops being a nuisance and starts shaping what you do.
The fix: a fan-out/fan-in thread pool. ThreadPool.map takes an array, a callable, and a thread count. It chunks the input, spawns one thread per chunk, joins them all, and stitches results back in input order:
| |
Order preservation is the key detail. Results get appended by job index, not by arrival time. Downstream code assumes players come back in the same sequence they went in.
The harder work was the prerequisite: making every callable pure. No global mutations as side effects. Explicit inputs in, explicit outputs out. That forced an audit of the entire generation pipeline. Rating functions wrote to shared stats dictionaries as a side effect. Aggregate values got accumulated by whatever code happened to touch them last. All of it had to become explicit return values. More code at the call site, but visible data flow instead of hidden mutations.
With purity handled, generate_class became two ThreadPool.map calls. First pass generates players. Second pass computes combine numbers:
| |
seeds[i] = randi() ^ (i * 0x9E3779B1) — XOR with a position-scaled constant gives each worker a unique seed. The comment says “deterministic seeds per index prevents RNG contention,” which is the right concern. But seed(int(seed_val)) inside the worker sets Godot’s global RNG state. Two threads calling seed() concurrently can overwrite each other. The per-index XOR prevents identical sequences, but the global mutation is still a race.
I noted the risk and shipped it. The pipeline was threaded and running. Whether the contention actually produces visible problems — that’ll take a benchmark to find out.