Reposted at http://www.netmag.com.tw/member/Article/010307a.htm
Use pre-compiled headers technology to accelerate compilation speed - as an example of Borland C Builder ---
Author: Hang Yat Sen Information programmer has a common experience: The more the greater the writing program, each change after completing the program to be recompiled to produce executable files, often take a long time for a frantic engineer Say, this is really a dream. The good idea is usually missed during this long compilation process. Of course, we can buy higher hardware make the compilation speed faster. However, the speed of hardware upgrades will never increase the speed of the process (this seems to be with each new version of Windows, we always think that the computer is getting more and more slowly, but the same work is the same. Several friends who use delphi to complain that time is too long, they always smile and advise me to use Delphi, because Delphi's compilation speed is really fast, and use delphi, almost can do any development tools on Windows. Get something. The author has always been a loyal C supporter (this is an emotional factor, there is no meaning of any language), I also spent a lot of time to study how to make the BCB written to compile faster, let more time Take the error or play an idea. This article is my research experience in this area. In this article, the author is all as discussed by the latest BCB 5.0. At first I assume that the reader did not use the experience of BCB, the first part of the article is specially written to beginners. If you are a BCB program designer with extensive experience, you can skip the first paragraph
Pre-homework
First, we first establish a simple console program to supply after you will be used. Please select File / New to turn on the New Item dialog, select console wizard in the new page: After pressing the OK button, the following screen appears: Please check on the Use VCL with the CONSOLE Application in the picture above. After the OK button is pressed, Console Wizard will automatically generate a program backbone, as shown below: Continue this program (Unit1.cpp) and the item file (Project1.bpr) Archive in an independent new directory, For example, we store them in the TEST directory, after the first storage, only a few files in the entire directory, as shown below: At any one, many friends will find that after each time use BCB compilation, it is in the project. There will be many files in the catalog, usually don't know which one should be deleted. With our just opened aware, when we pressed Project / make Project1, the file will be generated in the following picture: for future backups, we can delete some unnecessary files. The following table lists the relevant information:
Can the file function delete *. ~ *
Original code backup file
can
* .tds
Intermediate file
can
* .obj
Intermediate file
can
* .res
Compiled resource file
can
* .exe
Execute file
can
For some parts of the original code backup file, whenever we store files, as long as the file is originally existing in the directory, the IDE will change the original file to *. ~ *, Then rewrite the files you want to store. Hard dish in the hard disk. For example, Unit1.cpp, if the file is reused, the IDE changes the file name to the Unit. ~ CPP, then store the latest unit1.cpp back in the hard drive. If you think this is a bit annoying, even if you don't even have it, please select Tools / Editor Options to open the dialog box below: Take the Create Backup file this Check Box, so that Ide will It will not help us do the backup work. In addition, each CPP file will generate an OBJ file after compiling, so the more written, the resulting OBJ file will be more and more, and it is a bit troublesome when it is backup. This author provides a suggestion, which is to let the compiler uniformly put the compiled OBJ file below a directory. When we will delete all the files in this directory. In order to do this, please select Project / Option to turn the following dialog box: As shown, please fill in TMP in the intermediate output in the Directories / Conditional page, meaning that the compiler will compile the OBJ generated after compiling The file is unified to the TMP directory in the directory where the project is located. The last point is that BCB 5.0 is in the form of compilation on the background, so each time we press Make or Build, unless compiling problems, the compilation process dialog will automatically turn off. In order to observe the compilation process, please select Tools / Environment Options to open the following dialog: Please take the background compilation's check box, so compiled the process as the previous version before BCB 5.0, every time you build it on the screen. There is a compilation process dialog box in the middle, as shown below: OK, the front homework has been successful, then we have to start doing some test! First see pre-compiled headers technology
In the past, we wrote a C / C program, each file must be introduced into many system headers to introduce a lot of system headers to the program, then generate the execution unit via the connection. If our Project has two archives A.CPP and B.CPP, when we use the Getch () in A.CPP, we must write at the beginning of A.CPP. :
#include
#include
When the compiler compiles A.cpp, the compiler must compile CONIO.H once, and then compile the cono.h when compiling B.CPP. Therefore, once A.CPP introduces more headers as B.CPP, the compiler will waste a lot of time on the same header file. For example, if there are ten CPP files in our Project to introduce the same header gear, it represents the compiled time has 9/10 because it is wasted because it is used to compile the same header file. Therefore, BCB introduces Pre-Compiled Headers technology, mainly to solve this problem that the compiler will make more repetitive compilation work, resulting in a long problem. The so-called pre-compiled headers technology, in the memory of the author, the concept of introducing from BCB 3.0, in fact, is very simple, it means "pre-compiling the header file". In the case of the example we just mentioned, the compiler will compile A.CPP, it will first "cache" first because of the use of Pre-Compiled Headers technology, first "cache" first, then wait until When compiling B.CPP, the compiler will find that Conio.h has been compiled first, so the compiler directly takes the results of the conoe.h that just get up directly, so that it will save it. A large number of compile time, and the programming designer can say BYE BYE from this to the lengthy compilation process. In BCB, Pre-Compiled Headers technology is achieved through the compiler command #pragma HDRSTOP, known from the Help of BCB, and the header file before this compiler instruction will tell the compiler to use pre-compiled HEADERS technology to speed up compilation. But things don't seem to be as simple as we think, so the author will spend a long space to explore the impact of compiler command #pragma HDRSTOP to compile performance. PRE-Compiled Headers Impact on Compilation Speed - 1
First, we will delete all files generated in the compilation process in accordance with the
■ Code 1:
#include
Use build
Compiling the number of compiled lines compile time 4192667.90 After the second time 5106388.48 Using Make
Compiling the number of compiled lines compile time first 4192667.73 after the second time 00.14
■ Program 2:
#pragma hdrstop # include
#pragma argsusedint main (int Argc, char * argv []) {cout << "Hello World"; returnographic part is different from the program code 1
■ Test Result 2:
Use build
Compiling the number of compiled lines Compile time 4192665.60 After the second time 5106384.42 Using Make
The number of compile numbers compiles the first 4192665.86 After the second purchase 00.15 Remind the reader, the blank line is also calculated that the compiler is in the compiler, so everyone may have some micro gaps on the compiling line. The following conclusions can be obtained from the list:
For the two sets of tests, the first time was compiled regardless of Make or Build, its compilation speed is almost no different. But the second time after compilation, using Make's compilation speed overwhelming, and a lot is very fast, please readers should refer to some relevant books to introduce Make. Here, the author introduces O'Reilly (Taiwan Oujia) published by Managing Project With Make, this book seems to have a message that will appear in the website of Taiwan's Oura Gifts at www.oreilly.com.tw. 2. Put the header in the compiler command #pragma HDRSTOP, the time spent in the first compiler is longer than the compiler command #pragma HDRSTOP.咦? Before, it is not mentioned that using pre-compiled headers technology will speed up compilation speed. How to get the test speed after experiment? To find a reason, we can use Windows to start the function table Search / file or profile Function, search for BCB, limit the search date to your computer's current date, readers can find that once you put the header to the compiler command #pragma HDRSTOP, the lib sub-directory in the directory where the BCB is located. There will be two files of VCL50. # 00 and vcl50.csm, and the size of the file is quite big, but if the header is placed behind the compiler command #pragma HDRSTOP, the two files do not appear (if the reader Test the program code 1, then test the program 2, then the two files will still appear, because the two files do not be deleted because of re-opening Project or re-opening BCB, so the reader sees It may be the VCL50. # 00 and VCL50.csm generated by the previous compilation. In this way, we can boldly speculate: the reason why the header is put in the compiler instruction #pragma HDRSTOP will be longer, because the compiler spends an extra Kung Fu to produce this Two files. Because the compilation time displayed after compiling is not only a simple compilation time, the connection time is also included to generate the time spent on the execution file. Accurately, "From the start of compiling the original code to the time spent in the final implementation, the previous test data will give you a" using pre-compiled headers technology to slow down compilation " . The reader saw the data obtained from the test results 1, which is the author, except for the * .Obj, * .tds, * .Exe file generated when the author is deleted, except for the deletion of compilation, and except for the VCL50. # 00 And the two files of VCL50.CSM are measured. If the author does not delete the test code 1 / use build, the data generated by the test results 1 is generated by the data. # 00 and the VCL50.csm of the test results 1, the data will becomes: ■ Test Result 3:
Use build
Compiling the number of compiled lines compile time first 201723 13.57 second time after 20172313.98 Using Make
Compiling the compile time Compiling time The first 171.87 second time after 00.14 is caused by the cause difference? Because the author starts using the Build instruction to test the compilation performance, then turn off Project, delete the compilation process .obj, .tds, .exe file, then re-open Project, use the Make instruction to test the compilation performance, at this time, due to the previous BUILD, the VCL50 generated by Build, the two files are still left in the hard disk. In the middle, the compiler will be used directly! And so we see that the compiler is only compiled 17 lines. From this, we can prove that the two files of VCL50. # 00 and VCL50.csm are our so-called cache files, and their role is to allow the compiler to reduce the number of headers compiled to accelerate compilation. The conclusions obtained above tell us that if we will do pre-compiled headers technology to compile the compilation efficiency assessment of compilation performance, you should use the build directive during the first compilation, and use Make instructions after the second time. In order to accurately measure the improvement of Pre-Compiled Headers technology to compile performance, because we can see from the data, the Build instruction will let the compiler recompile once from the head until the head, so I only see Build. The result is meaningless. However, it is inevitable that it is inevitable to recompile the entire system. For example, once we turn the program from the Debug version into a release version, or turn the program from the Release version into a debug version, the first compile, even if we use make to compile the program, the time spent in the compiler and compiles using build. The result is the same, it is all over the end to recompile once. We can still use pre-compiled headers technology to make this compilation from beginning to tail faster, and will be mentioned later in this article. Pre-Compiled Headers Impact on Compilation Speed - 2
The test program in the previous paragraph has only a single program original file, then let's try to have a plurality of program original files in Project. In order to avoid the situation, we only test the situation in the project of two programs. First, please use File / New to add a unit: and save the file into unit2.cpp. At this time, there are more files in our Project, which is Unit2.h and unit2.cpp, and their contents are as follows: ■ Code 3:
Unit2.h # ifndef unit2h # define unit2h void test (void); #ENDIF unit2.cpp # include
Compiling the compile number of compiled lines Compile time (Build) 00.16 We observe the lib directory under the directory of BCB. At this time, there will be only two files before the test, and this test is actually There are also a profile, they are: VCL50.CSM, VCL50. # 00, VCL50. # 01. Such test results do not seem to conclusions, so after the second compilation, before the third compilation, revise the contents of Unit2.cpp as follows:
■ Code 4:
#include
The number of compile time compiling the number of compiled lines (Make) 1738432.69 is not the test result of the previous test results 5. Because there is no pre-compiled cache file, Unit2.cpp must be compiled from the beginning to the end after the modification process. Another test of the author is to use the compiler command #pragma hdrstop in unit2.cpp, then compile the execution file with the Make, let the compiler help us generate Unit2.cpp Cache files, the author does not delete any VCL50. # ??file. Then I took the compiler instruction in Unit2.cpp, although the changes of each original code will cause up to 173843, but after make a few times, if the author re-writer instruction #pragma HDRSTOP puts back the original location among Unit2.cpp (that is, in #include
With #include
Below), the results compiled after each modification process will be close to the test results 5.
Here we temporarily call the collections of all the introduced header files before the compiler command #pragma hdrstop, we boldly assume that the compiler will help us record this mark in the Cache file, Convenient the next compiler as an identification when compiling other files. We summarize a temporary conclusion based on the above assumptions, that is: each time the original code is compiled, it is generally recompiled all header files. However, if the original original file contains the compiler command #pragma hdrstop, then the compiler will look for VCL50. # ??, see if these archives meet the current "pre-compiled tag", if it is in line The compiler will directly reference the Cache left after compiling, so I save a lot of time to recompile the header file. If there is no Cache file that is in line with the tag, then the compiler will still start compiling all headers, and After compiling, you have a Cache file with the original original file with the same "Pre-compiled tag". When the prior compiled tag of the next original original, the compiler will directly quote this. Cache file. PRE-Compiled Headers technology work mode
In a paragraph in front, we get a hypothesis via inference. Next, we must prove whether our inquiry is correct. So we start modifying unit1.cpp and unit2.cpp, as follows: ■ Code 5:
Unit1.cpp #include
Compiling the compiled number compile time The first time (Build) 2022509.18 The program code according to the test and the test result 4 is based on the program code, in addition to the header introduced by the compiler instruction #pragma HDRStop, we put He has changed to two files. Compare this test result with the test results 4, the number of compilation lines is half. We will observe the lib directory under the directory of BCB. At this time, there will be only two files before testing, and they are: vcl50.csm, VCL50. # 00, respectively.嘿嘿 ~ Sure enough, as we expected, the compiler does not have a cache file for each file that uses the compiler command #pragma HDRSTOP, but a project, how many "pre-compiled tags", compile How many VCL50. # ?? files can be generated, which can effectively reduce space that needs to be spent in these Cache files. Through the above experiment, we prove that our previous assumptions are correct. However, there is a saying: "The compiler will generate a Cache file for each file that uses the compiler command #pragma HDRSTOP to accelerate compilation. Compiling markers produce different Cache files to accelerate compilation. As for the pre-compilation mark, what is the way to record? There are only four ways to be more likely to:
The data structure inside the registry compiler generates a pre-compiled marker database direct record in VCL50. # ?? You should remember that we can remove those Cache files yourself when we don't want to do it. So individuals think 1, 2 The approach is more impossible, and 3, 4 is a relatively possible way. The author's self-use tool detection whether registry has changed each time, and the results found that registry did not change; if it was the data structure inside the compiler, it is bound to build pre-compile information for each Project, but later the author himself After experimenting, it was found that these pre-compiled cache files can be "cross project", which means that these Cache files can make each project share, the compiler is only concerned about whether the pre-compiled mark is only worked. exist. However, when the author uses UltraEdit to open VCL50.CSM and VCL50. # ??, the author uses the UltraEdit search function to search for the two files in these two files. After the strings like Iostream or STDIO, the VCL50 can be introduced. .csm is more similar to 3 practice, while VCL50. # ?? Compare similar 4 practices, maybe Borland's engineers are useful! Since lack more detailed information, you can't do more detailed analysis, if Some readers have studied the results, don't forget to tell you! What is the pre-compilation mark?
The previous section proves that there is indeed a pre-compilation mark of this abstraction, but the pre-compiled mark is based on what principles? Before we do, the compiler instruction #pragma HDRStop All introduced headers before The collection of the file name is called "pre-compiling tags", really so? Let us do a few small experiments to prove whether it is what we assume. We modify the contents of the code 5, and do the following adjustments to the headers introduced by the compiler command #pragma HDRSTOP: ■ Adjust 1: Let the header file size different from
Unit1.cpp
Unit1.cpp
#include
Compiling the number of compiled lines Compile time First (build) 40445112.92 ■ Generated Cache file:
VCL50.CSM, VCL50. # 00, VCL50. # 01
■ Adjustment 2: Let the header file are arranged different
Unit1.cpp
Unit1.cpp
#include
Compiling the number of compiled lines Compile time first (build) 40445111.12 ■ Generated Cache file:
VCL50.CSM, VCL50. # 00, VCL50. # 01
■ Adjustment three: There is a space between the header file name
Unit1.cpp
Unit1.cpp
#include
■ Test results:
Compiling the number of compiled lines Compile time First (build) 2022515.40 ■ Generated Cache file:
Vcl50.csm, VCL50. # 00
From the conclusions measured from the above three adjustment program, we can get the following conclusions: pre-compiled markers are determined by the header introduced by the compiler instruction #pragma HDRSTOP, and two original original files are constituted Pre-compiled tags is:
The introduced header gear is exactly the order in which the header gear is used. If there is a compiler command #define, the contents of its contents must be the same (3-exported) The size of the header file file must be consistent ( Case-sensitive) As for the gap column, there is no impact on pre-compiling marks.
Includeall.h # include
Then modify unit1.cpp with unit2.cpp to: unit1.cpp # include "incrudeall.h" #pragma hdrstop # Pragma argsusedint main (int Argc, char * argv []) {cout << "Hello World"; return 0;} Unit2.cpp # include "incrudeall.h" #pragma hdrstop #include "unit2.h" void test (void) {printf ("test");}
All in all, in addition to blank line, each file is long before one model, and there is a difference to cause pre-compilation markers. So we can say: If you want Pre-Compiled Heads technology to the most, we should let each program in the program introduce the same header file, even if the card inside the header is in the program. It is also necessary to introduce among the original file. Oh! Next, some people will complain about me: "Then there is 100 original original files in my project, and each time I have introduced a header file in the original file, then I will correct other 99 A file that makes the header file introduced in each file, isn't it tired? " From this issue, we will lead to the best solution. This method can also accelerate us when using Build (GF to the tail recompilation), how to speed up compilation speed is: "All the #include instructions are all moved. Inside a single header, then each file inside Project is directly introduced into this single header file. "Compared with the addition of our independent header file, called includeall.h, this The contents of the file are as follows: after, as long as any primary gear needs to introduce a header file, it is possible to modify include Includeall.h, so that all program original files can be The same pre-compilation marker allows the pre-compiled headers technology to the most, and the programming designer can also save a lot of time to synchronize the original file. Of course, there is a more lazy person will ask: "Why don't you write #pragma hdrstop directly? This is not that we can play this line in the original file in every program?" Very good, the author is such a lazy engineer, but it actually tried it, found that there is a problem like this. The compiler does use pre-compiled headers technology, however, the compiler regards all program original code as a different pre-compilation mark, that is, if there is 40 files in Project to introduce nCludeall.h, Then there will be 40 cache, and the size is all the same, not only does not allow compilation speeds (because almost every original original file is re-compiled), but also because there is so many cache gears Time, also wasting the hard drive space, please don't write #pragma hdrstop inside in Includeall.h. As for why this happens? Finally let the pen on the BCB on-line help in the place where #pragma hdrstop is here: "Use this pragma directive only in Source Files. The Pragma Has No Effect When IS Used Although there is no counter-effect, the above line describes the compiler instruction #pragma HDRSTOP is no effect in the header file (even if we take the attachment from .h. Nothing to change. CPP is also used).
Do you only place the system header file before the compiler command #pragma hdrstop?
Please look back to see all the original original files we have used by us before this. Truck code 5, everyone will find that unit2.cpp is not placed before the compiler command #pragma hdrstop, then does the author's default unit2.cpp before the compiler command #pragma HDRSTOP? Let us do The Project Experimental Group, the program code is as follows: ■ Program 6-1: unit1.cpp #include
#include
#include
#include "unit2.h"
#pragma HDRSTOP
Void test (void)
{
Printf ("test");
}
Then we tried to build a look. Then we have a file to delete it, call Unit2.h and Unit2.cpp, change the contents of the file: ■ Code 6-2:
Unit2.h # iit2h #define unit2hvoid test (void); void test1 (void); #ENDIF unit2.cpp # include
■ Code 7-1:
Unit1.cpp # include
Unit2.h # ifndef unit2h # define unit2hvoid test (void); void test1 (void); #endif unit2.cpp #include
Compiling the number of compiled compilation times The first time the first (build) code 6-12022458.29 Second (Make) code 6-100.14 Third (Make) code 6-22022539.16Unit2.h after #pragma HDRStop
Compiling the number of compiled lines Compile time The first time (build) code code 7-12022588.65 Second (MAKE) code 7-100.16 Third (MAKE) code 7-2651.68 This test result represents what mean?
This test result is not only the system header file can be placed before the compiler command #pragma HDRStop, the programming designer's own header can also. In the header small more, the entire program has caused the original original file to recompile from the head to tail, which also enables the compiler to re-generate new Cache files (Note: The compiler overrides the VCL50 of the same pre-compilation mark. #? File, Instead of re-generation. In fact, this is not very thick. Otherwise, there will be a lot of cache files with the same pre-compilation, which can be bad!) In the initial stage of program development, the program header will often be modified, but the system The header file is almost no one will move to them, so before the entire system is not yet stable, try not to put the header file defined by the programself to the compiler command #pragma hdrstop. . Because of this, there is no acceleration program, but because the pre-compilation mark has not changed, the header file has changed, and forces the compiler to recompile these headers each time and generate new Cache files. (And first delete the CACHE file originally compiled as the same pre-compilation, so that the overall compilation time is longer). This issue is not obvious in our test, but readers can imagine. If we write the GUI program today, we often need to modify components and event handles on Form, each time a revision, is bound to head gears (Because of the addition / deletion of components, or new event handles the function to change the header file), if there is a lot of FORM in our project, it can not be wonderful !! So here The author's suggestion is: In the initial design of the program, first move the header file defined by these programming designers to the compiler command #pragma HDRSTOP (Idee, the Unit it produced is to be preset), wait until All categories, functions, and data structures in the entire system are approximately changing, so that these headers are moved to compiler command #pragma hdrstop, so that there will be a lot.
Let VCL-related headers can also enjoy the benefits of Pre-Compiled Headers
Compile number
173358 line
Compile time
4.05 seconds
VCL50.CSM size
7459 KB
Cache number
2 (vcl50.csm and vcl50. # 00)
Prior to this, all test examples were simple small. For the readers, although the conclusions above are very useful, if we want to develop a general GUI program? Let's take a look at a relatively practical example. We use File / New Application to re-build a GUI's Windows application. When we press Make, we have found that the results should be compiled as follows: If we put a lot of different components on the Form, we will find that the original original file is always They are only #include, only some related HPP files are introduced in the header file. So if we want to explore whether it can make the program that uses the VCL header file faster, then the place we have to have a brain is a VCL.h this file! So we open vcl.h, found that he only introduced VCL0. H, so we open vCl0.h again, 嘿嘿 ~~ Let us find interesting things.
In VCL0.h, we see several conditions compilation, as follows:
// Database related headers // #if defined (INC_VCLDB_HEADERS) #include
#define inc_vcldb_headers # def_vclext_headers # define inc_ole_headers #define inc_tl_headers # include
#pragma HDRSTOP
We will get the following results using Build.
Compile number
811680 line
Compile time
15.07 seconds
VCL50.CSM size 22483 KB
Cache number
2 (vcl50.csm and vcl50. # 00)
This experimental results also tell us:
If you use #define inc_vcldb_headers, # define inc_ole_headers, #define inc_ore_headers, you can ask the compiler to compile some of the headers that are not pre-compiled, so almost all the VCLs need to write a Windows program. All headers will be pre-compiled. If it is not used, try not to use four define. For example, if we don't use the database element, don't use #define inc_vcldb_headers; if you don't use the ATL (Active Template Library), don't use #define inc_atl_headers, because so, you will only waste extra Time compilation, but also because of the pre-compiling headers, caching makes Cache becomes large, which is just a waste of hard discs.
in conclusion
In the previous analysis and conclusions, the author provides a better solution, which is amended from the previous paragraph to "all the #include instructions all move all into a single header, then Project Every file inside is introduced directly into this single header file, we can change the contents of the header file of includeall.h.
includeall.h #ifdef USE_VCLDB #define INC_VCLDB_HEADERS #endif #ifdef USE_VCLEXT #define INC_VCLEXT_HEADERS #endif #ifdef USE_OLE #define INC_OLE_HEADERS #endif #ifdef USE_ATL #define INC_ATL_HEADERS #endif #include
Includeall.h #include
Note 1: PRE-Compiled Headers feature for the launch compiler
Whether the compiler uses Pre-Compiled Headers technology, it can be determined by two places:
Projec T / Option COMPILER Page: As shown, the preset compiler action is Cache Pre-Compiled Headers, and can also specify the file name of Cache. Readers can press F1 to find more messages, usually we don't have to get the settings of this place. Command Column Instruction: When we do not use the IDE to use the command column to compile Project, you can use the compiler parameter -h / -hu / -h- to control the related behavior of Pre-Compiled Headers.
Note 2: CB ON-LINE HELP Explanation of #pragma HDRSTOP
Move the mouse to the HDRSTOP of #pragma hdrstop and press F1, you can see the instructions for the on-line help on this compiler instruction.