C++ "Unity Builds"

Recently I heard about a method of increasing compile time for C++ projects called a “unity build”. For projects with a lot of source files compilation time can get extremely slow due to disk access. It’s especially bad when you have a maze of header dependencies as well. This website describes the issue and solution in detail: The Magic of Unity Builds.

For the project I have been working on the normal compile time is about 30 minutes for a clean and rebuild for a single platform/target. The unity build solution is to #include many CPP files into a single separate unity CPP file and then only compile the unity file. This improves both compilation and link times. Anecdotally I’ve heard it can cut build times in half or more.

That said, I’m not sure how well it integrates with distributed build systems like Incredibuild. I’m currently only doing local builds so I am unable to research or compare.

I’ve yet to implement this but am going to give it a shot very soon. If you are working on a huge software project and are suffering programmer inefficiencies due to compile times it would be good to look into. I’ve literally had days recently where I compiled for 3-4 hours out of an 8 hour day which was when I went over the edge and started looking for a solution.

2 Responses to “C++ "Unity Builds"”

  1. OJ says:

    Hi mate,

    I just thought I’d let ya know that I’ve put up a screencast which demonstrates the Unity Build configuration. It shows how much of speed boost that you can get with a simple project of just a few files.

    You can find it here. The code is there for download too.

    Hope that helps :)
    OJ

  2. Conrad says:

    I didn’t know about the “Unity Build” name, but I used this same trick ~12 years ago on a huge project that was being built on SGI systems, after finding that precompiled headers on the SGI compiler gave us almost no speedup.

    At first there was a lot of resistance among the developers (I was the young, “new” guy.) It’s not 100% transparent as you may have to rename some file-scoped variables to avoid name clashes from one file to another. But once compile-relink times were reduced from 30 minutes to 2-3 minutes, they all became believers.

    I’ve recently resurrected the same old trick again. Our projects are now cross-platform (Linux and Windows) so we use CMake to generate Makefiles on Linux and Visual Studio solution files on Windows. (CMake can also generate XCode and Eclipse solutions.) Here’s a CMake macro you can use to generate the files automatically.

    #
    # Usage: GENERATE_UNITY_BUILD_FILE( projectFilesVariableName )
    #
    # Takes the list of files in the project and makes an ${UNITY_BUILD_FILE_NAME} files that
    # #includes all of the source files. Adds ${UNITY_BUILD_FILE_NAME} to the list of source files.
    #
    # This is a compilation speedup technique suggested on various web sites such as:
    # http://buffered.io/2007/12/10/the-magic-of-unity-builds/
    # http://stackoverflow.com/questions/543697/include-all-cpp-files-into-a-single-compilation-unit
    #
    macro( GENERATE_UNITY_BUILD_FILE projectFilesVariableName )

    set( PROJECT_SINGLE_FILE ${CMAKE_CURRENT_BINARY_DIR}/${UNITY_BUILD_FILE_NAME} )

    # Generate list of project source files and set them to be excluded from the build
    FILE_LIST_BODIES( PROJECT_SOURCES ${${projectFilesVariableName}} )

    foreach( source_file ${PROJECT_SOURCES} )
    # Don’t add qrc_* generated files to ${UNITY_BUILD_FILE_NAME} – if more than one they clash with each other!
    if( NOT source_file MATCHES “qrc_.*” )
    # Mark source files as “header” so it will be excluded from the build
    SET_PROPERTY( SOURCE ${source_file} PROPERTY HEADER_FILE_ONLY true )
    endif( NOT source_file MATCHES “qrc_.*” )
    endforeach( source_file ${ARG_FILES} )

    # Add this derived file to the project
    LIST( APPEND ${projectFilesVariableName} ${PROJECT_SINGLE_FILE} )

    # Check to see if .parameters file matches ${PROJECT_SOURCES} – if so don’t need to regenerate ${UNITY_BUILD_FILE_NAME}
    SET( PROJECT_SINGLE_FILE_PARAMETERS ${PROJECT_SINGLE_FILE}.parameters )
    if( EXISTS ${PROJECT_SINGLE_FILE_PARAMETERS} )
    FILE( READ ${PROJECT_SINGLE_FILE_PARAMETERS} PROJECT_PARAMETERS )
    endif( EXISTS ${PROJECT_SINGLE_FILE_PARAMETERS} )

    if ( PROJECT_PARAMETERS STREQUAL PROJECT_SOURCES )
    # Do nothing since current parameters match parameters used to generate ${UNITY_BUILD_FILE_NAME}
    else ( PROJECT_PARAMETERS STREQUAL PROJECT_SOURCES )
    # Store the list of PROJECT_SOURCES in a .parameters file to see if need to regenerate ${UNITY_BUILD_FILE_NAME} or not
    FILE( WRITE ${PROJECT_SINGLE_FILE_PARAMETERS} “${PROJECT_SOURCES}” )
    # Now generate the single project source file
    FILE( WRITE ${PROJECT_SINGLE_FILE} “//==============================================================================\n” )
    FILE( APPEND ${PROJECT_SINGLE_FILE} “// This software developed by Stellar Science Ltd Co and the U.S. Government.\n” )
    FILE( APPEND ${PROJECT_SINGLE_FILE} “// Copyright (C) 2009 Stellar Science. Full, unlimited rights granted.\n” )
    FILE( APPEND ${PROJECT_SINGLE_FILE} “//——————————————————————————\n” )
    FILE( APPEND ${PROJECT_SINGLE_FILE} “\n” )
    FILE( APPEND ${PROJECT_SINGLE_FILE} “// THIS FILE WAS GENERATED AUTOMATICALLY BY CMAKE (GenerateUnityBuildFileMacro.cmake)\n” )
    FILE( APPEND ${PROJECT_SINGLE_FILE} “// ANY MANUAL CHANGES WILL BE LOST ON NEXT CMAKE!\n” )
    FILE( APPEND ${PROJECT_SINGLE_FILE} “\n” )
    if ( DEFINED GLEW_INCLUDE_DIRS )
    FILE( APPEND ${PROJECT_SINGLE_FILE} “// glew.h has to be #included before gl.h…\n” )
    FILE( APPEND ${PROJECT_SINGLE_FILE} “#include \n” )
    FILE( APPEND ${PROJECT_SINGLE_FILE} “\n” )
    endif ( DEFINED GLEW_INCLUDE_DIRS )
    foreach( source_file ${PROJECT_SOURCES} )
    # Don’t add qrc_* generated files to ${UNITY_BUILD_FILE_NAME} – if more than one they clash with each other!
    if( NOT source_file MATCHES “qrc_.*” )
    # Write the inclujde line to the newly-generated project single source file
    FILE( APPEND ${PROJECT_SINGLE_FILE} “#include \”" ${source_file} “\”\n” )
    endif( NOT source_file MATCHES “qrc_.*” )
    endforeach( source_file ${ARG_FILES} )
    endif ( PROJECT_PARAMETERS STREQUAL PROJECT_SOURCES )

    endmacro( GENERATE_UNITY_BUILD_FILE projectFilesVariableName )

    You can use it like this:
    SET( PROJECT_SOURCES file1.cpp file1.h file2.cpp, file2.h … )
    SET( UNITY_BUILD_FILE_NAME “_UnityBuildFile.cpp” )
    GENERATE_UNITY_BUILD_FILE( PROJECT_SOURCES )

    It will auto-generate the _UnityBuildFile.cpp and add it to your project while marking all the other files as “header only” so they won’t get compiled but you can still browse them in Visual Studio.

    Granted this only works if you’re using CMake, but I’m sure similar short, simple macros could be developed for other build systems (e.g. Visual Basic macros if you only use Visual Studio, shell scripts if you use UNIX Makefiles.)

Leave a Reply