pch.htm
来自「C++builder学习资料C++builder」· HTM 代码 · 共 785 行 · 第 1/4 页
HTM
785 行
<HTML>
<HEAD>
<TITLE>Improving C++Builder Build Times With Pre-Compiled Headers.</TITLE>
<META NAME="Author" CONTENT="Harold Howe">
</HEAD>
<BODY>
<CENTER>
<TABLE BORDER=0 CELLPADDING=0 CELLSPACING=0 WIDTH="640">
<TR><TD>
<H3>
Improving C++Builder Build Times With Pre-Compiled Headers.
</H3>
<P>
C++Builder is one of the fastest C++ compilers around, and probably the fastest Win32 C++ compiler in terms of compilation speed.
Despite the speed advantage that C++Builder holds over other C++ compilers, many Delphi programmers contort in agony while waiting
for a C++Builder project to compile. Anyone that has seen Delphi knows that it is blazing fast in comparison to any C++
compiler. Delphi can compile small example projects in less than a second, and large projects can be built in less than
five.
</P>
<P>
So why does Delphi hold such a speed advantage over C++Builder? Furthermore, is there anything that can be done to
improve the compilation speed of C++Builder? This article explains why C++ compilers are inherently slow, and
demonstrates a simple tactic to boost compile times in C++Builder.
</P>
<UL>
<LI><A HREF="#explanation">Why C++ Compilers are Slow</A>
<LI><A HREF="#pch" >How C++Builder Uses Pre-Compiled Headers To Reduce Compile Times</A>
<LI><A HREF="#vcl" >Explanation of Pre-Compiled Headers in a VCL GUI Project</A>
<LI><A HREF="#optimize" >Optimizing C++Builder's Use of Pre-Compiled Headers</A>
<LI><A HREF="#results" >Results</A>
<LI><A HREF="#notes" >Notes</A>
</UL>
</P>
<BR>
<H3>
<A NAME="explanation">Why C++ Compilers are Slow</A>
</H3>
<P>
In C++, you cannot call function from a source file unless that function has been previously defined or declared. So
what does this mean? Consider a simple example where function <TT>A()</TT> calls function <TT>B()</TT>.
<TT>A()</TT> cannot call <TT>B()</TT> unless a prototype for <TT>B()</TT>, or the function body for <TT>B()</TT>,
resides somewhere above the function body for <TT>A()</TT>. The code below illustrates this point.:
</P>
<pre>
<font color="navy">// declaration or prototype for B</font>
<b>void</b> B<b>(</b><b>)</b><b>;</b>
<b>void</b> A<b>(</b><b>)</b>
<b>{</b>
B<b>(</b><b>)</b><b>;</b>
<b>}</b>
<font color="navy">// definition, or function body of B</font>
<b>void</b> B<b>(</b><b>)</b>
<b>{</b>
cout <b><<</b> <font color="blue">"hello"</font><b>;</b>
<b>}</b>
</pre>
<P>
The code will not compile without the prototype for <TT>B()</TT>, unless the function body for <TT>B()</TT> is moved up
above <TT>A()</TT>.
</P>
<P>
Function prototypes serve a crucial role to the compiler. Every time you execute a routine, the compiler must insert
proper code to call the routine. The compiler must know how many parameters to pass the function. The compiler
must know if the function expects its parameters on the stack or in registers. In short, the compiler needs to know how
to generate the correct code to call the function, and it can only do this if it has seen a previous declaration or
definition for the function that is being called.
</P>
<P>
To simplify the prototyping of functions and classes, C++ supports a <TT>#include</TT> statement. The <TT>#include</TT>
directive allows a source file to read function prototypes from a header file prior to the location in code where
the prototyped functions are called. The <TT>#include</TT> directive plays an important role in Win32 C++ development.
Function prototypes for C RTL functions are provided in a standard set of header files. The Win32 API is prototyped in
a set of header files provided by Microsoft, and the classes and functions of the VCL are listed in header files that
come with C++Builder. You can't create a very useful Windows program without including header files provided by
Microsoft or Inprise.
</P>
<P>
Header files help implement C++ type checking in a manner that is easy to manage for the programmer. However, this
benefit comes at a huge cost. When the compiler runs across a <TT>#include</TT> directive,
it literally opens the included file and inserts it into the current file. The compiler then parses the included file
as if it was part of the file that it was already compiling. So what happens if the first header file includes yet
another file? The compiler will suck in that file and start parsing it. Imagine what happens when 10, 20, or
even 100 files are included? While this number of include files may sound large, it isn't unrealistic when you start
adding up the Windows SDK header files and all of the VCL header files.
</P>
<P>
To demonstrate how the compiler branches off and translates included files, consider the following code example. This
is a simple console mode program that I built using the Console Wizard from the Object Repository. In order to test this
code, select Options-Project-Compiler and turn off pre-compiled headers.
</P>
<pre>
<font color="navy">// include some standard header files</font>
<font color="green">#include <stdio.h></font>
<font color="green">#include <string.h></font>
<font color="green">#include <iostream.h></font>
<font color="green">#include <windows.h></font>
<font color="green">#pragma hdrstop</font>
<font color="green">#include <condefs.h></font>
<font color="navy">//-----------------------------------------------</font>
<b>int</b> main<b>(</b><b>)</b>
<b>{</b>
printf<b>(</b><font color="blue">"Hello from printf.\n"</font><b>)</b><b>;</b>
cout <b><<</b> <font color="blue">"Hello from cout"</font> <b><<</b> endl<b>;</b>
MessageBeep<b>(</b><font color="blue">0</font><b>)</b><b>;</b>
<b>return</b> <font color="blue">0</font><b>;</b>
<b>}</b>
</pre>
<P>
When I build this project with C++Builder, the build progress dialog reports that the project contains 130,000 lines of
code. 130 thousand lines! How can that be? The source file only contains about four lines of code. The 130,000 lines
were contained in STDIO.H, STRING.H, IOSTREAM.H, WINDOWS.H and all of the other header files that are included by
these four header files. In this example, the compiler spent the vast majority of its time processing header files.
</P>
<P>
Now let's investigate what happens when you have multiple CPP files in a project. Building off of the existing
project, let's add a unit to the console program that we already have. Add a simple function to this
second unit. Then alter the first CPP file so it will call the new function.
</P>
<pre>
<font color="navy">//-------------------------------------------------------</font>
<font color="navy">// UNIT1.CPP</font>
<font color="green">#include <stdio.h></font>
<font color="green">#include <string.h></font>
<font color="green">#include <iostream.h></font>
<font color="green">#include <windows.h></font>
<font color="green">#include "Unit1.h" </font><font color="navy">// prototype A() in unit1.h</font>
<font color="green">#pragma hdrstop</font>
<b>void</b> A<b>(</b><b>)</b>
<b>{</b>
printf<b>(</b><font color="blue">"Hello from function A.\n"</font><b>)</b><b>;</b>
<b>}</b>
<font color="navy">//-------------------------------------------------------</font>
<font color="navy">//-------------------------------------------------------</font>
<font color="navy">// PROJECT1.cpp</font>
<font color="green">#include <stdio.h></font>
<font color="green">#include <string.h></font>
<font color="green">#include <iostream.h></font>
<font color="green">#include <windows.h></font>
<font color="green">#include "Unit1.h"</font>
<font color="green">#pragma hdrstop</font>
<font color="green">#include <condefs.h></font>
<font color="navy">//-------------------------------------------------------</font>
USEUNIT<b>(</b><font color="blue">"Unit1.cpp"</font><b>)</b><b>;</b>
<font color="navy">//-------------------------------------------------------</font>
<b>int</b> main<b>(</b><b>)</b>
<b>{</b>
printf<b>(</b><font color="blue">"Hello from printf.\n"</font><b>)</b><b>;</b>
cout <b><<</b> <font color="blue">"Hello from cout"</font> <b><<</b> endl<b>;</b>
A<b>(</b><b>)</b><b>;</b>
MessageBeep<b>(</b><font color="blue">0</font><b>)</b><b>;</b>
<b>return</b> <font color="blue">0</font><b>;</b>
<b>}</b>
<font color="navy">//-------------------------------------------------------</font>
</pre>
<P>
Now build the project. If you turned off pre-compiled headers before building the project, you will see that the
compiler progress dialog reports 260,000 lines of code when the build finishes. Notice that the compiler had to
translate the same set of header files in two different CPP files. We know from the previous example that these header
files place a burden of 130,000 lines on the compiler. The second CPP file places the same 130,000 line burden on the
compiler, for a grand total of 260,000 lines of header files. Imagine how this line count would rapidly grow in a large
project. The burden of processing the same group of header files over and over can greatly increase compile times.
</P>
<BR>
<H3>
<A NAME="pch">How C++Builder Uses Pre-Compiled Headers To Reduce Compile Times</A>
</H3>
<P>
The engineers at Borland realized that they could decrease build times by designing a compiler that did not process the
same header files over and over during the build. To achieve this goal, Borland C++ 3.0 introduced the concept of
pre-compiled headers. The idea behind pre-compiled headers is relatively simple. When the compiler processes a set of
header files for one particular source file, it saves the compiled image of the header files on the hard drive. When
that set of header files is required by another source file, the compiler loads the compiled image instead of processing
the header files a second time.
</P>
<P>
Let's modify our console mode program to see how pre-compiled headers can impact build times. The code itself should
already be fine. We just need to turn the project's pre-compiled headers option back on. Select Options-Project-Compiler
and check the <I>Use pre-compiled headers option</I> or the <I>Cache pre-compiled headers option</I>. Enter PCH.CSM in
the pre-compiled header filename box. Do a full rebuild of the project once you change the settings.
</P>
<P>
During the build, pay special attention to the compiler progress dialog. You should see that the compiler processes
130,000 lines of code when it compiles PROJECT1.CPP, but when it compiles UNIT1.CPP, it only process 20 lines of code.
The compiler generates a pre-compiled image when it parses the first source file, and that pre-compiled image is used
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?