Beyond CMAKE_UNITY_BUILD_BATCH_SIZE: Alternative Strategies for Faster CMake Builds
What it is
- It sets the default maximum number of source files that can be combined into a single "unity source file" during a unity build.
CMAKE_UNITY_BUILD_BATCH_SIZE
is a CMake variable introduced in version 3.16.
Unity Builds in CMake
- This reduces the compiler overhead associated with processing individual files.
- It works by combining multiple source files (typically translation units) into a single source file before compilation.
- Unity builds are an optimization technique in CMake that can improve compilation times for large projects.
CMAKE_UNITY_BUILD_BATCH_SIZE in Action
- You can set
CMAKE_UNITY_BUILD_BATCH_SIZE
to a specific value during your CMake configuration process. - This value becomes the initial limit for all targets that have unity builds enabled (using the
UNITY_BUILD
target property).
- You can set
Unity Build Creation
- When a target with unity builds enabled is created, CMake considers the value of
CMAKE_UNITY_BUILD_BATCH_SIZE
. - It distributes the original source files for that target across as many unity source files as needed to stay within the batch size limit.
- When a target with unity builds enabled is created, CMake considers the value of
Example
set(CMAKE_UNITY_BUILD_BATCH_SIZE 100) # Set the default limit to 100 source files per unity source
target_compile_definitions(my_target PRIVATE UNITY_BUILD)
In this example:
- The maximum number of source files in any unity source file for
my_target
will be 100 (unless overridden formy_target
specifically).
Key Points
- Choosing the optimal batch size depends on various factors like project size, compiler behavior, and hardware characteristics. Experimentation might be necessary to find the best value for your project.
- You can adjust the batch size for specific targets using the
UNITY_BUILD_BATCH_SIZE
target property. CMAKE_UNITY_BUILD_BATCH_SIZE
is a global variable affecting all targets with unity builds enabled by default.
Additional Considerations
- Consider other optimization techniques like precompiled headers if unity builds don't provide significant gains.
- Unity builds can be beneficial for projects with many small source files, but might not be as effective for projects with large or complex files.
Setting a Global Default Batch Size
# Set the global default batch size to 50 files
set(CMAKE_UNITY_BUILD_BATCH_SIZE 50)
# All targets with `UNITY_BUILD` enabled will inherit this limit unless overridden.
target_compile_definitions(my_target1 PRIVATE UNITY_BUILD)
target_compile_definitions(my_target2 PRIVATE UNITY_BUILD)
Overriding the Default for a Specific Target
# Global default is still 50 files
set(CMAKE_UNITY_BUILD_BATCH_SIZE 50)
# Set a lower batch size of 20 files for `my_target3` only
target_compile_definitions(my_target3 PRIVATE UNITY_BUILD)
set_target_properties(my_target3 PROPERTIES UNITY_BUILD_BATCH_SIZE 20)
Disabling Unity Builds for a Target
# Global default is still 50 files
set(CMAKE_UNITY_BUILD_BATCH_SIZE 50)
# Target `my_target4` will not use unity builds, regardless of the global setting
target_compile_definitions(my_target4 PRIVATE UNITY_DISABLE_BUILD)
# Check if the current project supports unity builds
if(CMAKE_UNITY_BUILD)
message(STATUS "Unity builds are enabled with a default batch size of ${CMAKE_UNITY_BUILD_BATCH_SIZE}")
else()
message(STATUS "Unity builds are not enabled")
endif()
Precompiled Headers (PCH)
- PCHs are source files containing precompiled declarations of frequently used headers. Compiling these declarations once saves time by avoiding repetitive compilation in each translation unit.
- Use the
PRECOMPILED_HEADER
target property to configure PCH.
- Use the
Parallel Compilation
- Modern compilers often support parallel compilation to utilize multiple CPU cores or threads.
- Use compiler flags like
-j
or-fopenmp
to enable this (consult your compiler documentation for specific options).
- Use compiler flags like
Out-of-Source Builds
- An out-of-source build keeps your source code separate from the build directory. This avoids unnecessary recompilation of source files when only CMakeLists.txt changes.
- Use the
CMAKE_CURRENT_BINARY_DIR
variable to reference the build directory within your CMake scripts.
- Use the
Caching Build Results
- Some build systems like Ninja (often used with CMake) can cache compilation results, reducing recompilation when source files haven't changed.
- Consider using Ninja as your build system if it's not already the default.
Refactoring Code
- Code with better modularity and reduced dependencies can lead to faster compilations. Break down large source files into smaller, more focused units.
Choosing the Right Approach
- Refactoring Code
Improves build speed and code maintainability in the long run. - Caching Build Results
Reduces redundant builds when source files haven't changed. - Out-of-Source Builds
Improves build speed by avoiding unnecessary recompilation of unchanged source files. - Parallel Compilation
Take advantage of multi-core architectures if your build process is CPU-bound. - Precompiled Headers
Useful when many translation units include the same headers repeatedly. - Unity Builds
Good for projects with many small source files, but might not be effective for large files or complex code.
- It's often beneficial to experiment with different approaches to find the best combination for your project. Analyze build times to identify bottlenecks and target them with appropriate optimization techniques.