Precompiled Headers and Unity (Jumbo) Builds in upcoming CMake

As you might have heard Qt is moving away from qmake in favor of CMake. Some people have raised concerns with the fact that CMake generates projects that build in longer time than qmake.

For the last couple of weeks I have been busy with improving CMake upstream in regards to compilation speed.

Precompiled headers

Qmake can make use of precompiled headers, while CMake doesn't have support for them natively. It's enough to search after "precompiled headers CMake" in your favorite search engine to find tens of projects that provide this functionatily via a CMake module.

CMake has a bug report named Support for precompiled headers opened 14 years ago.

In his C++Now2017: Effective CMake talk Daniel Pfeifer mentioned that he had a prototype adding precompiled headers support, namely CMake MR 984. At 1h:19m:18s we can see how precompiled headers as usage requirements would look in CMake:

target_precompile_headers(Foo
  PUBLIC
    "foo.h"
  PRIVATE
    <unordered_map>
)

His prototype had only support for Visual Studio and Xcode generators, nothing for Ninja or Makefiles.

Based on his work I've completed CMake MR 3553, which also adds support for Ninja and Makefiles generators.

Precompiled headers support in compilers can be split into two categories:

  • Visual C++ style: for a pch.h / pch.c pair you will get two generated files pch.pch and pch.obj.
    You need to link to the generated pch.obj file.
  • GCC style: for pch.h / pch.h.c pair you will get one generated file pch.h.gch.
    There is no obj file to link to, and if you try to link to the pch.h.gch file you will get an error.

With both styles supported in CMake, I did a CMake build of Qt Creator on my Windows 10 machine running on a Lenovo AMD Ryzen CPU:

  • MinGW GCC 8.1.0 – 22.09% speed-up
  • Visual C++ 2017 – 37.28% speed-up
  • Clang-cl 8.0.0 – 30.66% speed-up

The results in a chart:

qtcreator-pch-no-pch-time

Because every target will get its own precompiled header, the build directory size have changed a bit:

qtcreator-pch-no-pch-size

The code that I had to write to add precompiled headers support in Qt Creator's CMake files is here.

Note for ccache users: you need to set up ccache sloppiness.

Unity (Jumbo) builds

While working on adding support for precompiled headers I learned how to add source files internally in the CMake project. Therefore it was easy to come up with unity (jumbo) source files, which include the original source files in batches in form of:

#include "source_file1.cpp"
#include "source_file2.cpp"
/*...*/
#include "source_file8.cpp"

The CMake MR 3611 adds support for Unity (Jumbo) Builds in CMake.

With precompiled headers one could just change a few CMake lines and get a 20-40% speed increase without changing any source files. With unity builds it's not always the case. ODR (One Definition Rule) errors are popping up everywhere.

Qt Creator's source code is not compatible with unity builds. I took Speedcrunch for testing.

Speedcrunch also doesn't work out of the box with unity builds. Patch can be found here.

Setting up Unity builds with CMake is as easy as passing -DCMAKE_UNITY_BUILD=ON in the CMake command line. The default batch size is 8.

I configured Speedcrunch in Release mode, and compiled only the speedcrunch target, and got the results:

  • MinGW GCC 8.1.0 – 29.33% speed-up
  • Visual C++ 2017 – 53.50% speed-up
  • Clang-cl 8.0.0 – 23.61% speed-up

The results in a chart:

speecrunch-normal-unity-time

The build directory size is a bit smaller with Unity builds, because of less object files created.

speecrunch-normal-unity-size

Both features are under code review on Kitware side. We can do something about the build times now, until C++20 Modules will change the game.


Blog Topics:

Comments