Fix Slow Build Times: 8 Proven Optimization Tips
Slow game builds are not a mystery — they are a pipeline with a few expensive stages, and the fix is always the same: measure first, then attack the stage that is actually slow. The usual culprits are asset import and processing, IL2CPP C++ generation in Unity, shader-variant compilation, and full rebuilds triggered by a cold cache. This guide walks through eight techniques that work across Unity, Godot, MonoGame, and GameMaker — keep the build cache warm, use incremental IL2CPP builds, strip shader variants, enable parallel compilation, and match your build configuration to its purpose. No invented percentages: measure your own before-and-after with a build report or an MSBuild binary log.
Slow game builds are not a mystery. A build is a pipeline with a handful of stages, and almost every slow build is slow because of one of four stages: asset import and processing, IL2CPP C++ generation in Unity, shader-variant compilation, or a cold cache forcing a full rebuild. The fix is always the same two-step: measure which stage is actually slow, then attack that stage. This guide is the eight techniques that work — and an explanation of why no one can give you a percentage improvement for your project.
A warning up front: almost every "this trick makes builds X percent faster" number you read online is invented. Unity, Microsoft, and Godot document how their build systems work, but they do not publish headline percentages, because the result depends entirely on your project. The only honest number is the one you measure yourself. So the first technique is the one that makes the other seven useful.
The four things that make a build slow
Before the techniques, the diagnosis. A game build is roughly: import and convert assets, compile scripts, compile shaders, package everything for the target platform. Slowness lives in one of these:
| Stage | What happens | Why it is slow |
|---|---|---|
| Asset import | Textures compressed, audio encoded, models processed, cached as binary | Compression and encoding are CPU-heavy; a clean cache re-does all of it |
| Script compilation | C# (or C++) compiled to the target runtime | IL2CPP adds a C++ generation and compile step Mono does not have |
| Shader compilation | Each shader variant compiled for each graphics API | Variants multiply combinatorially — n keywords can mean up to 2^n variants |
| Packaging | Built content packed into the platform's format (APK, AAB, executable) | Each extra target platform adds its own pass |
Unity's own documentation is blunt about shader variants: a large number of variants increases build times, file size, runtime memory, and loading times. The combinatorial part is the killer — ten independent on/off shader keywords can produce up to a thousand variants, each compiled separately.
Now the techniques.
1. Measure first, always
If you do not know which stage is slow, you will optimize the wrong thing. Every major toolchain gives you a way to read the build's timing breakdown — use it before changing anything.
- Unity: the
BuildReportAPI exposes what was built and how large each part is; theEditor.logfile contains the per-step timings that actually tell you where the minutes went. The Build Report Inspector package visualizes the report. - .NET / MonoGame / MSBuild: run
dotnet build -blto emit a binary log, then openmsbuild.binlogin the MSBuild Structured Log Viewer. It shows per-project and per-task timings, which is the single best way to find the slow project in a solution. - Godot: run with
--verboseto log import activity, and watch the.godot/imported/cache status.
Record a before number, change one thing, record an after number. Without that, you are guessing.
2. Keep the build cache warm
The most common cause of a suddenly slow build is a cold cache. Unity stores imported assets under Library/; Godot stores them under .godot/imported/; MSBuild and MGCB keep intermediate object files between builds. Delete any of these and your next build re-imports and re-compiles everything.
The fix is a habit, not a setting: do not delete the cache directory between builds. If you use a clean-build step in CI, make sure it only cleans what it must. If your team shares import work, run a cache server — Unity's is the Unity Accelerator, the successor to the old standalone Cache Server — so one developer's import work is reused by the rest of the team.
The Asset Import Pipeline v2 has been the default in Unity since 2020.1, and it is materially faster than the legacy pipeline on top of benefiting from the Accelerator. If you are on an old Unity version still using the v1 pipeline, upgrading the pipeline is the highest-leverage change you can make.
3. Use incremental IL2CPP builds
IL2CPP — Unity's system that converts managed C# into C++ — is the reason a Unity mobile or console build can take many times longer than a desktop Mono build. It is also the part most worth configuring.
Unity supports incremental IL2CPP builds: when you preserve the IL2CPP output directory (the Library/il2cpp_* folders) between builds, the C++ compiler only recompiles the files that changed, instead of the whole generated codebase. The official guide on optimizing IL2CPP build times walks through the settings. Turn it on for any platform where you build repeatedly.
The broader rule: reserve IL2CPP for builds where it earns its cost. IL2CPP gives better runtime performance and harder-to-reverse binaries, which is why it is standard for mobile and console release builds. For fast iteration on a desktop dev build, switch the scripting backend to Mono and skip the C++ step entirely.
4. Strip shader variants
If shader compilation is your bottleneck — and for any project with a lot of material effects, it often is — the lever is variant count, not the shaders themselves.
Unity builds shader variants at build time, and each variant is compiled separately for each graphics API you target. Variants come from multi_compile and shader_feature keywords, and they multiply: a shader with ten independent on/off keywords can produce up to a thousand variants. Most projects only ever use a fraction of them.
Unity gives you two ways to cut the count. Graphics Settings has automatic stripping options, and the IPreprocessShaders and IPreprocessBuild editor-script APIs let you strip variants programmatically in a build pre-process. Strip the variants you do not use and both your build time and your runtime memory improve. The same logic applies in Godot and Unreal — count your variants, cut the unused ones.
This is the direct parallel to the Blueprint performance guide: in every engine, the fastest code is the code you never compile.
5. Run parallel compilation
Modern build systems can compile independent projects and files in parallel, and a surprising number of teams leave that switched off.
For .NET, MonoGame, and any MSBuild-based pipeline, pass -m (or -maxcpucount) to build projects in parallel across CPU cores. MSBuild 18.6 also added in-process multi-threaded build support. If your solution has many projects, the difference between serial and parallel compilation is large — and the binary log from technique 1 will show you exactly how many projects are being built and whether they parallelize.
For Unity, the editor compiles scripts and imports assets with internal parallelism that you generally do not tune directly; the levers are the ones above (cache, IL2CPP, shaders) rather than a build flag.
6. Use iterative content builds
If your engine treats content (levels, audio, textures) as something it builds separately from code, make sure it is rebuilding only what changed.
- Unity Addressables and Asset Bundles support iterative content builds — rebuild only the bundles that changed rather than the whole content set on every player build. Note the Addressables build report is disabled by default precisely because generating it adds time; leave it off unless you are diagnosing.
- MonoGame's MGCB (MonoGame Content Builder) is designed to rebuild only changed assets by tracking file timestamps, turning source assets into runtime
.xnbfiles. It runs as part ofdotnet build. Historical bugs have caused full rebuilds in some setups; if you suspect that, the MSBuild binary log will show MGCB reprocessing assets it should have skipped.
The principle is the same as incremental code compilation: never redo work whose inputs have not changed.
7. Match the configuration to the purpose
A common slowdown is using the wrong build configuration for the job. Builds have a purpose — fast iteration during development, or a shippable binary for release — and the configuration should follow the purpose.
- During development: use the Debug configuration (or your engine's equivalent) and the fastest scripting backend. In Unity that means Mono. These configurations skip optimizer passes and produce builds meant for quick iteration. In MonoGame/.NET, the Debug configuration emits unoptimized assemblies with debug symbols.
- For release: switch to Release, enable IL2CPP or AOT where the platform wants it, and accept the longer build time because runtime performance and binary size now matter more than build convenience.
The mistake is leaving a project on the heaviest configuration for everyday work. If every test build is a release build, every test build is slow for no reason. Match the configuration to what the build is for.
8. Put your build on fast storage
The unglamorous one. Builds are heavily input/output bound — thousands of small file reads and writes. A build that takes many minutes on a spinning hard drive can drop substantially on an NVMe SSD. This is not a tweak you configure; it is a property of where your project and build output live. If your build directory is on a slow volume, move it. No engine documents a specific speedup because the result depends on the drive, but the direction is universal engineering consensus.
Engine-specific quick reference
| Engine | Fastest wins |
|---|---|
| Unity | Asset Import Pipeline v2 + Accelerator, incremental IL2CPP, shader-variant stripping, Mono backend for dev |
| Godot 4 | Keep .godot/imported/ intact, multi-threaded import, --verbose to diagnose |
| MonoGame / .NET | dotnet build -m for parallelism, -bl to diagnose, MGCB incremental content builds, keep the cache |
| GameMaker | Use the VM (not YYC) target for fast dev iteration, reserve YYC for release |
Where Egmatic fits
Egmatic is a 2D game IDE on MonoGame, and its build story is the MonoGame/.NET story: dotnet build with incremental compilation, the MGCB content pipeline for assets, and standard MSBuild parallelism and binary logs. What Egmatic changes is the iteration loop before the build — the live preview means most of your "does this feel right" questions never need a build at all. Fewer builds needed means fewer slow builds sat through, which is the cheapest optimization available. When you do build, the techniques above apply unchanged.
Common mistakes
- Optimizing before measuring. If you do not know which stage is slow, you will fix the wrong one. Always start with a build report or binary log.
- Cleaning the cache between builds. A cold cache is the most common cause of a slow build. Stop deleting
Library/or.godot/out of habit. - Using IL2CPP for every build. IL2CPP is for release and for platforms that require it. For dev iteration, Mono is faster.
- Ignoring shader variants. Variant count multiplies combinatorially. A few
multi_compilekeywords can quietly add thousands of compile units. - Building for every platform every time. Each extra target adds a full packaging pass. Build for the platform you are testing on.
- Running serial builds. If your toolchain supports parallel compilation and you have CPU cores, use them.
When this applies
These techniques apply to any small team or solo developer whose build has become slow enough to interrupt work. If your build takes under a minute, the cache is warm and the configuration matches — keep it that way, and do not optimize what is not broken. If it takes many minutes, measure first, then walk the list: warm the cache, switch to incremental IL2CPP, strip shader variants, parallelize, and use iterative content builds. The improvement you get will be specific to your project, which is exactly why the only honest benchmark is the one you run yourself.