Your Abstractions Are Killing Your FPS!
By Fuat Can Köseoğlu,
• Abstractions make code maintainable, but in performance-critical paths, their costs add up fast. In hot loops, every nanosecond matters The Hidden Cost of Indirection • Every interface, virtual method, or delegate adds indirection — the CPU must look up where to jump at runtime. • Each call adds a few nanoseconds in isolation (2–12 ns in microbenchmarks, depending on CPU and optimization). • When stacked — interface → virtual → delegate → property getter — in some cases can reach tens of ns per op (if not optimized away). • In real games, cache misses often dominate, but these costs still compound. • Example: 500 units × 4 interface calls × 30 FPS = 60,000 calls/sec — and that’s before you even factor in memory traffic or game logic. The Fat Interface Problem Not just CPU jumps — memory layout hurts too. • Struct array (1000 items): ~8KB, contiguous, cache-friendly • Interface refs (1000 items): 8KB refs + object overhead + scattered heap allocations • “Clean” abstractions can fragment memory and nuke cache efficiency When Clean Code Hurts Performance • SOLID encourages small interfaces, but resolving many contracts dynamically inside hot loops can add measurable overhead. • IMovable + IRotatable + IDamageable + IRenderable = extra dispatch per entity. • Clean code is great in business logic — but in inner loops, it can cost frames. Unity’s Twist Unity adds overhead on top: • Native → managed boundary crossing • Virtual dispatch to Update() • Per-MonoBehaviour method dispatch overhead Even with JIT/IL2CPP, the per-MonoBehaviour bookkeeping doesn’t disappear — with thousands of them, this snowballs quickly. What Works in Practice Successful games often trade abstractions for raw speed in hot paths: • Direct state access > dependency injection • Arrays > repositories • Structs > interfaces • Cache-friendly data > strict DRY • Specialized code > generic abstractions This isn’t “bad” engineering — it’s context-appropriate engineering. Key Takeaways • Indirection layers compound in hot paths • Contiguous data beats scattered objects • Use abstractions in business logic, keep hot loops lean But wait — doesn’t JIT/IL2CPP optimize most of this away? 👉 Have you actually seen abstraction overhead show up in your profiler, or does the compiler optimize it away? 👇 Follow for O(1) access to daily game dev tips!