Cristian Adam

Precompiled Headers and Unity (Jumbo) Builds in upcoming CMake

Published Thursday August 1st, 2019
13 Comments on Precompiled Headers and Unity (Jumbo) Builds in upcoming CMake
Posted in Build system, C++ | Tags: , , ,

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.

Do you like this? Share it
Share on LinkedInGoogle+Share on FacebookTweet about this on Twitter

Posted in Build system, C++ | Tags: , , ,

13 comments

Philip Schuchardt says:

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

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

Chris says:

Qbs is (way) faster and scale (way) better than cmake+ninja (any Makefile based build will inherently be slower).
See https://lists.qt-project.org/pipermail/qbs/2019-July/002509.html

Eike Ziller Eike Ziller says:

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 CMAKE_AUTOGEN_PARALLEL would improve that.

AK says:

What about cotire? Looks quite simple.

https://github.com/sakra/cotire

Cristian Adam Cristian Adam says:

I used Cotire for a PCH proof of concept for Qt Creator’s CMake build. See https://codereview.qt-project.org/c/qt-creator/qt-creator/+/266791

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 says:

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 Cristian Adam says:

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 says:

Can targets share the same pch?

Richard W says:

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 says:

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

https://blog.qt.io/blog/2018/10/29/deprecation-of-qbs/

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

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 says:

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.

Commenting closed.

Get started today with Qt Download now