Unity/Jumbo builds are a way of speeding up C/C++ compilation by compiling multiple c/cpp files as a single object file. Speed improvements can come from not having to re-parse and compile shared headers. Even in projects that use pre-compiled headers, there is an overhead of processing the pre-compiled header which can be avoided with Unity/Jumbo builds which we will observe below.
Enabling Unity/Jumbo Builds in Visual Studio 2019 & 2022:
In Visual Studio, open the Project Configuration Manager. Then under Configuration Properties > Advanced > Enable Unity (JUMBO) Build set it to Yes.
This will add the following entry to your .vcxproj:
The generated Unity files will be in your projects $(IntDir) by default. They’ll be something along the lines of ‘unity_XA9SRNK1NRU62EJK.cpp’ so you can inspect them and see exactly what’s going on and what files are being compiled.
Additional useful options:
Once Unity/Jumbo support has been enabled, it will show additional options under Configuration Properties > C/C++ > Unity Build. These can be changed per file. For example under the ‘Code Snippet To Add Before Include’ you can add the following to show in the output what files each generated Unity file contains:
To show the benefits of Unity/Jumbo builds I’ve created a simple test project which contains 50 .cpp files, each including the same large header and a simple function returning an constant integer.
The header file included in each file is as follows, which contains a fair bit of code to parse and compile.
#pragma once #include <Windows.h> #include <iostream> #include <vector> #include <map> #include <unordered_map> #include <stdio.h> #include <stdint.h> #include <assert.h>
- Default: 16.5 seconds
- Unity/Jumbo: 2.2 seconds
- pch: 2.1 seconds
- Unity/Jumbo & pch: 1.5 seconds
Results above are from an Intel i5-8265U, 8GB DDR4 @ 2400MHZ. Visual Studio 2022, MSVC 14.33.31629, x64 bit, multithreaded compilation enabled.
So the results above show that comparing normal multithreaded compilation of each individual cpp file vs a combining them into a few Unity/Jumbo .cpp files we see a significant improvement. However the results compared to just using pre-compiled headers are fairly similar, as you’d expect considering each .cpp file is almost empty and is just including the same headers. What’s interesting is when we combine both unity & pre-compiled headers, we see a nearly 25% improvement in compile speed. We can attribute that to the overhead of processing the PCH for each .cpp file, even though it already contains ‘pre-compiled’ code.
Lets take a look at the use of Unity/Jumbo builds in a real world project next.
Unity/Jumbo build support in assimp:
assimp is a library to import and export various 3d-model-formats including scene-post-processing to generate missing render data. https://github.com/assimp/assimp
assimp does not work out of the box when you just enable Unity/Jumbo build support so it required some fixes in a few places to compile and for certain .cpp files, it was simpler to exclude them from being used in the Unity builds. I’ll go through some of the common errors you’ll encounter as a result in the next section.
- Default Build: ~75 seconds
- Unity/Jumbo Build: ~35 seconds
It took some effort to get it fully working and even then, some files were not part of the unity build process but the improvements in compile time are significant. If you scale this to a much larger project, you could easily shave a significant amount of time off your builds.
Common Unity/Jumbo build errors:
Due to the nature of including multiple .cpp files into a single object/compilation unit majority of the problems will come from code in one CPP file bleeding into others. Common errors will come from ‘using namespace MyNamespace;’, static/global variables sharing the same name and #defines.
If any errors prove too difficult to fix, a fallback can always be to exclude the specific file from Unity/Jumbo build support. This is done by setting Configuration Properties > C/C++ > Unity Build > Included In Unity File to False.
error C2872: ‘Type’: ambiguous symbol
This error could be due to a Type existing in multiple namespaces and both namespaces being used. Workarounds for these kind of errors can be to fully quality the namespace in the cpp files
error C2011: ‘MyClass’: ‘class’ type redefinition
This error could be from certain header files not containing header guards or a #pragma once so end up being included multiple times.
error C2084: function ‘int FunctionA(void)’ already has a body (function redefiniton)
Sometimes you’ll come across little helper functions written as a global/static in a cpp file and can exist in multiple cpp files. The proper way to fix this would be to forward declare the function in a header and only include the implementation once.
error C2374: ‘MyInt’: redefinition; (variable redefiniton)
Similar to the function problem above, often static variables can have the same name. If they need to be different, rename them or alternately expose them via headers.
Common Windows.h issues:
While running some tests, I ran into issues as a result of Windows.h being exposed to lots more .cpp files. This brought out some common issues such as ‘min’ and ‘max’ being defined in Windows.h which can break use of std::min / std::max. This error was fixed for me by defining ‘NOMINMAX’.
Other issues arose as a result of some other #defines in Windows.h. Assimp for example has some functions called ‘CreateDirectory’ which Windows.h can #define as CreateDirectoryW.
So often it can be a good idea to try and keep platform specific code files together as part of the build process.
Support for Unity (Jumbo) Files in Visual Studio: https://devblogs.microsoft.com/cppblog/support-for-unity-jumbo-files-in-visual-studio-2017-15-8-experimental/
Working with Unity/Jumbo builds: https://austinmorlan.com/posts/unity_jumbo_build/