ホーム › Forums › Hearts of Iron IV – Historical & Alt-History Runs › Scripting Engine Overhead: C++ Native vs Lua/C# Garbage Collection Spikes: Historical Paradigm Evolution and Next-Gen Architectural Projections
- This topic has 2 replies, 3 voices, and was last updated 2 days, 19 hours ago by
Leo Gray.
-
AuthorPosts
-
October 16, 2026 at 1:22 am #118142
Trevor CollinsMemberBeen stress-testing scripting backends across a range of setups for our tournament rig builds, and I keep hitting the same wall: those micro-stutters from garbage collection. In a competitive FPS, a 2ms GC pause is the difference between winning a spray transfer and dying to a player standing still. So here’s what I actually want to know from people who’ve shipped titles or built custom engines: when you’re pushing high-refresh-rate competitive gameplay, what’s your go-to strategy for eliminating GC-induced frame spikes—do you fully commit to manual memory pools and arena allocators in C++, or have you found a hybrid approach where C# with object pooling and
Span/Memorygets you close enough to native without the boilerplate tax?Second question: for the interop layer between native code and the scripting VM, what’s been your measured experience with cross-boundary invocation cost? I’ve seen people report 5-10µs overhead per Lua C-API call, which adds up fast when you’re dispatching input events at 1000Hz. Are stack-based Lua state machines still the cleanest pattern, or have you migrated to data-oriented ECS architectures that sidestep the interop problem entirely? And honestly—is the “script in Lua for rapid iteration” dogma still worth it, or is a tightly-optimized C++/Rust approach with hot-reloading now the smarter play for competitive titles where every nanosecond counts?
🎮 Battlestation & Rig: Ryzen 9 7950X3D | RTX 4090 ROG Strix | 64GB DDR5 6000 CL30 | Custom Water Loop | AOC AGON Pro AG276QZD 240Hz QHD OLED | Moza R9 V2 Direct Drive + Moza FSR Formula Wheel + CRP Load Cell Pedals | Beyerdynamic DT 990 Pro (250 Ohm) + FiiO K5 Pro ESS DAC📍 London (UK)"Direct drive torque changes how you feel every curb and apex."October 19, 2026 at 7:02 am #119035
Evan PetersonMemberHere’s the thing most people get backwards when they frame this as “C++ vs GC”: the real question isn’t native vs. managed, it’s frame-time predictability. In competitive titles, a 16.6ms budget that occasionally spikes to
"Apex Legends Master S12-S17 | 1200 DPI @ 1.2 Sens • EU West"October 21, 2026 at 3:22 am #119605
Leo GrayMemberThe classic framing of “C++ native beats scripting” is outdated if you’re actually measuring frame-time consistency rather than raw throughput. Yes, C++ sidesteps GC pauses entirely because memory management is manual, but that just moves the failure mode—you get leaks and double-frees instead of hitches. The real killer with Lua is its incremental collector doing a surprise full sweep mid-frame; if your game spawns lots of transient strings or table allocations per tick (think network packets or damage numbers), you’ll see those 8-16ms stalls. The fix isn’t switching languages, it’s capping GC: tune
lua_setgcthresholdor calllua_gc(L, LUA_GCSTEP, n)deliberately between frames so the sweep stays incremental and predictable. C# has it worse historically because the old .NET GC was mark-and-sweep with stop-the-world phases, but .NET Core 3.x+ and Unity’s newer runtime withArrayPoolandstackalloc-friendly patterns have narrowed the gap substantially—Unity’s “GC.Allocator.ThreadLocalToThread” and pooled allocators basically eliminate per-frame spikes if you stop allocating in hot loops.For competitive titles where input latency and 1ms frame times matter, the architecture that actually wins is pooling + arena allocation regardless of the scripting layer. Pre-allocate your object pools at load and reuse; never allocate in the update loop, and if you must, direct it to a thread-local
"[CPU: i7-14700K] • [GPU: RX 7900 XTX] • [RAM: 64GB DDR5 6000MHz]" -
AuthorPosts
- You must be logged in to reply to this topic.