C + + code has always been high-performance in its run-time face the world, but the speed of the compilation, but only a low-key part. For example, I now work in the source code, even using IncrediBuild transfer nearly hundreds of machines, a complete build also takes four hours, horror!!! Although usually development generally do not need to do a full build in the local, but compile a few related projects will be enough for you to wait for a long time (foreigner tube This is called monkey around, quite image). Think of the scene of a few years working on a single core 2.8GHZ-put a book in front of you, a little build button, and read the book for a while.
As you can imagine, the speed of compilation is likely to be a bottleneck in the development process if it is not taken seriously. So why is C + + so slow to compile?
I think one of the most important reasons is the C + + basic "header file-source file" compilation model:
- Each source file as a compilation unit, may contain hundreds or even thousands of files, and in each compilation unit, these header files will be read from the hard disk, and then be parsed again.
- Each compilation unit produces an obj file, and so the obj files are link together, and the process is difficult to parallelize.
The problem here is the repeated load and parsing of countless header files, as well as dense disk operations.
Here are some ways to speed up the compilation from various angles, mainly on the above-mentioned key issue.
One, the code angle
- Use the predecessor declaration in the header file instead of directly containing the header file.
Don't think you're just adding a header file, this effect may be infinitely magnified due to the "included" attribute of the header file. So, do everything possible to make the header file leaner. Most of the time, the pre-declaration of a namespace class will be more painful, and the direct include will be more convenient, to resist this temptation; class members, function parameters and so on as far as possible with reference, pointers, to create conditions for the predecessor declaration.
- Using Pimpl mode
Pimpl is all called private implementation. The interface of the traditional C + + class is confused with the implementation, and Pimpl this approach makes the interface and implementation of the class completely separate. Thus, as long as the public interface of the class remains the same, modifications to the class implementation always only need to compile the CPP, and the header files that the class provides to the outside world are much leaner.
- Highly modular
Modularity is low coupling, which is to reduce interdependence as much as possible. There are actually two levels of meaning here. One is between the file and the file, a header file changes, try not to cause other files to recompile, and the other is engineering and engineering, the modification of a project, try not to cause too many other projects to compile. This requires that the header file, or the content of the project must be single, do not put everything into the inside, causing unnecessary dependence. This can also be said to be cohesive bar.Take the header file as an example, do not put two unrelated classes, or no link to the macro definition in a header file. The content should be as single as possible, so that the files that contain them will not contain the unwanted content. Remember that we have done such a thing, the most "hot" in the code to find the header files, and then divided into several separate small files, the effect is quite impressive.
In fact, the refactoring we did last year, separating the many DLLs into the UI and core two parts has the same effect-improving development efficiency.
- Delete Redundant header files
Some code after ten years of development and maintenance, the number of people to handle, it is possible to include a useless header files, or repeated inclusion of the phenomenon, remove these redundant include is quite necessary. Of course, this is mainly for CPP, because for a header file, one of the include redundancy is difficult to define, depends on whether the final compilation unit is used, and this may appear in a compilation unit in use, and in another compilation unit in the case of useless.
Previously wrote a Perl script to automatically remove these redundant header files, in a project to remove up to more than 5,000 include.
- Pay special attention to the inline and template
These are two "advanced" mechanisms in C + +, but they force us to include implementations in the header file, which makes a significant contribution to adding the contents of the header file and slowing down the compilation speed. Before using, weigh it.
second, comprehensive skills
- Precompiled header File (PCH)
Put some common but infrequently changed header files in the precompiled header file. This way, at least in a single project you don't need to load and parse the same header file over and over in each compilation unit.
- Unity Build
The Unity build approach is simple, include all of the CPP in one CPP (All.cpp), and then just compile all.cpp. So we only have a compilation unit, which means that we do not need to repeat the load and parse the same header file, and because only one obj file, at the time of the link does not need so dense disk operation, it is estimated to have a 10x improvement, see this video to feel the way and speed it.
- CCache
compiler cache, the result of the last compilation of the cache, so that rebuild in the same situation to maintain the same results, greatly improve the speed. We know that if it is a build, the system will compare the source code and the target code time to decide whether to recompile a file, this method is not completely reliable (such as the last version of the code from SVN), and the principle of ccache judgment is the content of the file, relatively reliable more. Unfortunately, Visual Studio does not yet support this feature-in fact, you can add a new command, such as the cache build, between build and rebuild, so that rebuild can be basically unused.
- Don't have too many additional include directories
The compiler locates the header file for your include, which is based on the Include directories you provide. As you can imagine, if you provide 100 include directories, and a header file is in the 100th directory, the process of locating it is very painful. Organize your inclusion directory and keep it simple.
Third, compiling resources
To speed up, either reduce the task, or deploy more people, the first two aspects are to reduce the task, and in fact, the increase in the speed of compiling this piece, the extra staff still have a very important role.
- Parallel compilation
Buy a 4-core, or 8-core CPU, each time a build, is 8 files parallel to the compilation, that speed, watching is cool. If your boss disagrees, let him read this article: Hardware is Cheap, programmers is expensive
- A better disk
We know that the speed of compilation is much slower because of the disk operation, and in addition to minimizing disk operations, we can also do is to speed up the disk. For example, when the above 8 cores work, the disk is likely to become the biggest bottleneck. Buy a 15000-turn disk, or an SSD, or RAID0, in short, the faster the better.
- Distributed compilation
The performance of a machine is always limited, the use of free CPU resources in the network, and dedicated to compile the build server to help you compile to fundamentally solve the problem of our compilation speed, think of the original build 1 hours project can be done within 2 minutes, You know you must not without it--incredibuild.
- In parallel, you can actually do this.
This is a more extreme situation, if you use the IncrediBuild, the final compilation speed is still not satisfied with, how to do? In fact, as long as out of the frame of thinking, the speed of compilation can also have a qualitative leap-the premise is that you have enough machines:
Suppose you have solution A and solution b,b dependent on a, so you must build B after a. Where a, b build takes 1 hours each, then a total of 2 hours. But must b build after a? Out of this frame of mind, you have the following options:
- Start build A and B at the same time.
- A builds successfully, although the build of B failed, but all just failed on the last link.
- Re-link project in B.
In this way, by having the build of A and the compilation of b parallel, and finally link the project in B, the entire compilation speed should be controlled within 1个小时15分钟.
In addition, this book talks a lot about this: large-scale C + + programming.
How to speed up compilation of C + + code (RPM)