Wednesday, October 13, 2010

Qt Creator: How to compile it significantly faster

I prefer to use latest Qt and latest Qt Creator - to use new features and also to see that nothing is broken in the current version. So I recompile Qt and Qt Creator from the scratch quite often - to be sure that there are no any bugs due to the incorrect compilation.

So I want to compile them as fast as possible. My Qt configure is so:
-debug -opensource -no-qt3support -no-phonon-backend -no-phonon -no-multimedia -no-webkit -no-svg -no-javascript-jit -optimized-qmake -no-cups -reduce-relocations -no-xmlpatterns -no-xvideo -nomake demos -nomake examples -declarative -qt-sql-sqlite -developer-build -qtnamespace MegaQtNameSpace
Briefly - do not compile a lot of modules which will not be needed for me, and also specify Qt namespace (to see any errors related to misuse this feature). I use Ubuntu 10.10, so I had just to create a copy of config.status - and copy it to any new shadow build folder.

I think that most interesting thing with such Qt config is that the Qt compilation requires almost the same time like the Qt Creator compilation. About 22-23 minutes on my home laptop and about 16-18 minutes - on the work laptop. (I compile Qt Creator like any usual Qt application: qmake -r; make -j 2)

What might be the reason? Even reduced Qt has to be much more than Qt Creator.

I decided that the answer is a usage of precompiled headers in Qt sources by default and lack of usage this feature in Qt Creator.

What are precompiled headers?

Quote from Qt documentation:
"Precompiled headers are a performance feature supported by some compilers to compile a stable body of code, and store the compiled state of the code in a binary file. During subsequent compilations, the compiler will load the stored state, and continue compiling the specified file. Each subsequent compilation is faster because the stable code does not need to be recompiled."

Also by the documentation - it should be very easy to add precompiled headers to any Qt project, almost everything will be done by the framework:
  1. Create a special header file which has to contain stable and static code
  2. Define the PRECOMPILED_HEADER variable in the project file
And that's all.

There is just some note, that "all platforms that support precompiled headers have the configuration option precompile_header set". It looks like a bug in the documentation, I became confused by this sentence, at least in the current Qt master branch grep -r precompile_header qt/mkspecs shows that precompile_header is predefined only for WinCE and Win32.

Ok, anyway it can be enabled manually by:
CONFIG *= precompile_header
.

Which exactly precompiled header has to be used for Qt Creator? Lets check what is used for Qt.

  1. QtCore - just 9 base Qt headers
  2. QtGui - the same base + 12 classes related to QtGui

Suddenly for me - just a small amount of headers are included. I think that almost the same might be used for Qt Creator.
Lets just add there, for example, QPointer and QScopedPointer headers. Small shell command to see the most popular includes (has to be improved, of course):
egrep -rsh ^[[:space:]]*#[[:space:]]*include * | sed 's/^ *\(.*\) *$/\1/' | sort | uniq -c | sort -n | less

Qt Creator consist of different projects (libraries and plugins), so PRECOMPILED_HEADERS has to be defined in a lot of sub projects. How it can be done?

This is my solution:
Merge request for Qt Creator.

So general Qt headers will be used in any libraries (by qtcreatorlibrary.pri), and extended with Gui part - in any plugin (by qtcreatorplugin.pri). Also it is possible to use any other precompiled headers, like it is done for Botan (it is pure C++ library, so even general Qt header are almost useless).

As it is already said, precompiled headers are not enabled by default for linux gcc, so it has to be done manually like here:
qmake "CONFIG*=precompile_header" -r ../qt-creator

Ok, what are the results? Is compiling become faster?

Ubuntu 10.10 x64, CPU T7500, 3GB RAM

Without precompiled headers (average, ~10 tries):
real ~23m
user ~36m
sys 4:30

With precompiled headers (average, ~20 tries):
real 14:30
user 21m
sys 3:30

Ubuntu 10.10 x64, CPU T9600, 4GB RAM

Without - ~16 minutes, with - ~11:30.

Required space: 1.3 GB without pch and 2.8 GB with pch.

Ok, the result is already great for me, about 40% faster than before.

I tried to add more headers to qtcreator_pch.h or botan_pch.h, but again - a compilation became slower. I think the reason is that the .pch file become too big and might not be cached good enough.


I decided to check, what is the result for Windows.

Windows Vista x86, CPU P8400, 2GB RAM

Precompiled headers are enabled by default for Windows, so the next command line was used to compile the version without precompiled headers:
qmake "CONFIG-=precompile_header" -r ../qt-creator

Qt 4.7.0 release (Qml Designer will not be compiled).
Visual Studio 2008.

With precompiled headers: ~20 minutes (~2 GB)
Without precompiled headers: ~70 minutes (~1 GB)
So the compilation time is reduced to ~30%. Amazing.
And I recalled why I switched to use Linux. :)

About a required additional hard disk space for precompiled headers. Again, Qt Creator consist of a lot of subprojects, so any of this projects - will create its own precompiled header, even if it is the same qtcreator_pch.h. It requires time and disk space. I hope later Qt will be able to handle such situation also (if supported by compiler).

Ok. I hope I will be able to check Mac OS X results also, it is interesting - is it worse or better than Windows. :)

So, the conclusion: a lot of space for contributions to Qt Creator in different areas. E.g. I think a plugin-helper to create precompiled headers might be useful (at least count the used headers - or more useful, up to the special wizard).

Update 25.10:
It looks like precompiled headers sometime are not the good choice, especially on 'cool compiler PCs'. E.g. Linux PC which might compile Qt Creator by make -j 10 in 12 minutes - with enabled compiled header might became totally disappointing, compilation time might grow to 30-40 minutes. It looks like the reason is cache - requirements to the available free memory are much more stricter now.

I think the solution might be something like 'global' precompiled headers (for most Qt Compiler subprojects - precompiled headers are almost the same, so it really might be compiled just once). Problem - different defines for such precompiled headers, has to be investigated how exactly it has to be done correctly.