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

Commenting for this post has ended.

?
Philip Schuchardt
0 points
69 months ago

I'm curious how CMAKE compares to QBS build times?

I know this is super old, but https://www.qt.io/blog/2012... shows that qbs has 80% performance improvement over Makefiles.

?
Chris
0 points
69 months ago

Qbs is (way) faster and scale (way) better than cmake+ninja (any Makefile based build will inherently be slower).
See https://lists.qt-project.or...

?
Vincent
0 points
69 months ago

Perhaps you are interested in this post https://lists.qt-project.or...

?
Eike Ziller
0 points
69 months ago

We now have a real-life project which can be compiled with qmake, CMake and Qbs: Qt Creator.
CMake and Qbs are both not really, perfectly complete. CMake does not build translations and the separate cpaster, dmgbuild and qtc-askpass tools yet, Qbs does not build cplusplus-keywordgen and valgrind-fake. All in all I'd say the build result is comparable though.

So just go ahead and try it on your setup.
Note that any numbers you get don't mean that: 1) one of the build systems might not do things that the other should do too, 2) the corresponding build system files couldn't be optimized for the use case you test.

On my 4-core, 24GB RAM iMac there is no relevant difference between CMake/Ninja (3.15) and Qbs (1.13), both do full builds of Qt Creator in around 10 minutes, qmake in 15. Numbers for a "no-change incremental build" are around 1 second for CMake, 2 seconds for Qbs, 5 seconds for qmake. The second email thread posted above seems to hint to some kind of performance bottleneck with CMake or the CMake files when running on a high performance setup (30 cores / 256GB RAM). Maybe setting CMAKEAUTOGENPARALLEL would improve that.

?
AK
0 points
69 months ago

What about cotire? Looks quite simple.

https://github.com/sakra/co...

?
Cristian Adam
0 points
69 months ago

I used Cotire for a PCH proof of concept for Qt Creator's CMake build. See https://codereview.qt-proje...

There you can see that the changes that I needed to do were more extensive than what I needed for the native CMake approach.

Cotire does the same thing, adds a source file, compiles it as precompiled header, changes the compiler flags of all source files in the project to reference the precompiled header. If you go to the cotire's github page, you can see in the issues list all the problems that people have with it.

Having precompiled headers in native CMake means that the precompile headers functionality is always tested when something is being added to CMake, always kept up to date, tested on more platforms etc.

?
Chris
0 points
69 months ago

Hi Cristian,

When you say that the build directory size changed "a bit", do you realize that it is a 5x increase for clang, a 10x increase for MSVC and a 20x increase for MinGW? That is massive.

?
Cristian Adam
0 points
69 months ago

Hi Chris, that is correct, the increase is massive. I've done a small change to the Qt Creator commit that adds PCH support, to add it only for targets that have more than 3 source files.

With this change the Visual C++ 2017 x64 build directory size went down from 16.10GiB to 8.71GiB. That's 45% smaller, which should be the same for the other compilers. The compilation speed-up remained at 37%.

?
Danila
0 points
69 months ago

Can targets share the same pch?

?
Richard W
0 points
69 months ago

I can see how jumbo builds can be beneficial in CI environments where you do a fresh build from scratch every time. But I wonder how useful they are during daily development. I suppose that jumbo builds would evaporate the benefits of incremental builds when (re-)building a product with many translation units after changing only a single one.

?
Oleg
0 points
69 months ago

"As you might have heard Qt is moving away from qmake in favor of CMake" - may be from qbs, not qmake? This article:

https://www.qt.io/blog/2018...

promises that "support for qmake will continue unaffected", while qbs will not survive 2019.

?
Tobias Hunger
0 points
69 months ago

Qt 6 itself will most likely get built with cmake. There has not been a final decision yet AFAIK, but that is what people work on right now. Qt Creator even has patches in its master branch so that developers can build it using cmake. Qt is moving towards cmake at this time.

On the other hand you will be able to build your projects with qmake for a very long time and you will find support for that build tool in Qt Creator -- just as you do right now. That part is unaffected.

?
HGH
0 points
69 months ago

What CMake needs a is a case sensitive, object oriented (targets are objects, which have properties - instead of loose collection of "variables"), with a common style guide, flexible language that is possible to debug, which also allows easy automation of tasks.

N
Nicolas Moreaud
0 points
55 months ago

If you are stuck with QMake, you can give this tool a try : https://github.com/nmoreaud/qmake-unity (I wrote it).