⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 ch12.htm

📁 c++语言操作手册
💻 HTM
📖 第 1 页 / 共 5 页
字号:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN"><HTML><HEAD>	<META NAME="Author" Content="Steph Mineart">	<META HTTP-EQUIV="Content-Type" CONTENT="text/html;CHARSET=iso-8859-1">	<TITLE>ANSI/ISO C++ Professional Programmer's Handbook - Chapter 12 - Optimizing Your Code</TITLE>	<link rel="stylesheet"  TYPE="text/css" href="/includes/stylesheets/ebooks.css"></head><BODY TEXT="#000000" BGCOLOR="#FFFFFF"><CENTER><H1><img src="/publishers/que/series/professional/0789720221/button/que.gif" WIDTH="171" HEIGHT="66" ALIGN="BOTTOM" BORDER="0"><BR>ANSI/ISO C++ Professional Programmer's Handbook</H1></CENTER><CENTER>  <P><A HREF="/publishers/que/series/professional/0789720221/index.htm"><img src="/publishers/que/series/professional/0789720221/button/contents.gif" WIDTH="128"HEIGHT="28" ALIGN="BOTTOM" ALT="Contents" BORDER="0"></A>   <HR></CENTER><H1 align="center">12</H1><h1 align="center"> Optimizing Your Code</h1><address>by Danny Kalev </address><ul>  <li><a href="#Heading1">Introduction</a>     <ul>      <li><a href="#Heading2">Scope of This Chapter</a>     </ul>  <li><a href="#Heading3">Before Optimizing Your Software</a>   <li><a href="#Heading4">Declaration Placement</a>     <ul>      <li><a href="#Heading5">Prefer Initialization to Assignment</a>       <li><a href="#Heading6">Relocating Declarations</a>       <li><a href="#Heading7">Member-Initialization Lists</a>       <li><a href="#Heading8">Prefix Versus Postfix Operators </a>     </ul>  <li><a href="#Heading9">Inline Functions</a>     <ul>      <li><a href="#Heading10">Function Call Overhead</a>       <li><a href="#Heading11">Benefits of Inline Functions</a>       <li><a href="#Heading12">What Happens When a Function that Is Declared inline         Cannot Be Inlined?</a>       <li><a href="#Heading13">Additional Issues of Concern</a>       <li><a href="#Heading14">The Do's and Don'ts of inline</a>     </ul>  <li><a href="#Heading15">Optimizing Memory Usage</a>     <ul>      <li><a href="#Heading16">Bit Fields</a>       <li><a href="#Heading17">Unions</a>     </ul>  <li><a href="#Heading18">Speed Optimizations</a>     <ul>      <li><a href="#Heading19">Using a Class To Pack a Long Argument List</a>       <li><a href="#Heading20">Register Variables</a>       <li><a href="#Heading21">Declaring Constant Objects as const</a>       <li><a href="#Heading22">Runtime Overhead of Virtual Functions </a>       <li><a href="#Heading23">Function Objects Versus Function Pointers</a>     </ul>  <li><a href="#Heading24">A Last Resort</a>     <ul>      <li><a href="#Heading25">Disabling RTTI and Exception Handling Support</a>       <li><a href="#Heading26">Inline Assembly</a>       <li><a href="#Heading27">Interacting with the Operating System Directly</a>     </ul>  <li><a href="#Heading28">Conclusions</a> </ul><hr size=4><h2><a name="Heading1"> Introduction</a></h2><p>One often-heard claim during the past 30 years is that performance doesn't   matter because the computational power of hardware is constantly dropping. Therefore,   buying a stronger machine or extending the RAM of an existing one can make up   for the sluggish performance of software written in a high-level programming   language. In other words, a hardware upgrade is more cost-effective than the   laborious task of hand-tuning code. That might be correct for client applications   that execute on a standard personal computer. A modestly priced personal computer   these days offers higher computational power than a mainframe did two decades   ago, and the computational power still grows exponentially every 18 months or   so. However, in many other application domains, a hardware upgrade is less favorable   because it is too expensive or because it simply is not an option. In proprietary   embedded systems with 128K of RAM or less, extending the RAM requires redesigning   the entire system from scratch, as well as investing several years in the development   and testing of the new chips. In this case, code optimization is the only viable   choice for satisfactory performance.</p><p>But optimization is not confined to esoteric application domains such as embedded   systems or hard core real-time applications. Even in mainstream application   domains such as financial and billing systems, code optimization is sometimes   necessary. For a bank that owns a $1,500,000 mainframe computer, buying a faster   machine is less preferable than rewriting a few thousand lines of critical code.   Code optimization is also the primary tool for achieving satisfactory performance   from server applications that support numerous users, such as Relational Database   Management Systems and Web servers.</p><p>Another common belief is that code optimization implies less readable and harder   to maintain software. This is not necessarily true. Sometimes, simple code modifications   such as relocating the declarations in a source file or choosing a different   container type can make all the difference in the world. Yet none of these changes   entails unreadable code, nor do they incur any additional maintenance overhead.   In fact, some of the optimization techniques can even improve the software's   extensibility and readability. More aggressive optimizations can range from   using a simplified class hierarchy, through the combination of inline assembly   code. The result in this case is less readable, harder to maintain, and less   portable code. Optimization can be viewed as a continuum; the extent to which   it is applied depends on a variety of considerations.</p><h3> <a name="Heading2"> Scope of This Chapter</a></h3><p>Optimization is a vast subject that can easily fill a few thick volumes. This   chapter discusses various optimization techniques, most of which can be easily   applied in C++ code without requiring a deep understanding of the underlying   hardware architecture of a particular platform. The intent is to give you a   rough estimate of the performance cost of choosing one programming strategy   over another (you can experiment with the programs that are discussed in the   following sections on your computer). The purpose is to provide you with practical   guidelines and notions, rather than delve into theoretical aspects of performance   analysis, efficiency of algorithms, or the Big Oh notation. </p><h2> <a name="Heading3"> Before Optimizing Your Software</a></h2><p>Detecting the bottlenecks of a program is the first step in optimizing it.   It is important, however, to profile the release version rather than the debug   version of the program because the debug version of the executable contains   additional code. A debug-enabled executable can be about 40% larger than the   equivalent release executable. The extra code is required for symbol lookup   and other debug "scaffolding". Most implementations provide distinct debug and   release versions of operator <tt>new</tt> and other library functions. Usually,   the debug version of <tt>new</tt> initializes the allocated memory with a unique   value and adds a header at block start; the release version of <tt>new</tt>   doesn't perform either of these tasks. Furthermore, a release version of an   executable might have been optimized already in several ways, including the   elimination of unnecessary temporary objects, loop unrolling (see the sidebar   "A Few Compiler Tricks"), moving objects to the registers, and inlining. For   these reasons, you cannot assuredly deduce from a debug version where the performance   bottlenecks are actually located.</p><blockquote>   <hr>  <b>A Few Compiler Tricks</b>   <p> A compiler can automatically optimize the code in several ways. The <i>named     return value</i> and <i>loop unrolling</i> are two instances of such automatic     optimizations.</p>  <p> Consider the following code:</p>  <pre><tt>  int *buff = new int[3];</tt><tt>  for (int i =0; i&lt;3; i++)</tt><tt>    buff[i] = 0;</tt></pre>  <p> This loop is inefficient: On every iteration, it assigns a value to the     next array element. However, precious CPU time is also wasted on testing and     incrementing the counter's value and performing a jump statement. To avoid     this overhead, the compiler can unroll the loop into a sequence of three assignment     statements, as follows: </p>  <pre><tt>  buff[0] = 0; </tt><tt>  buff[1] = 0; </tt><tt>  buff[2] = 0; </tt>  </pre>  The named return value is a C++-specific optimization that eliminates the construction   and destruction of a temporary object. When a temporary object is copied to   another object using a copy constructor, and when both these objects are cv-unqualified,   the Standard allows the implementation to treat the two objects as one, and   not perform a copy at all. For example   <pre><tt>class A</tt><tt>{</tt><tt>public:</tt><tt>  A();</tt><tt>  ~A();</tt><tt>  A(const A&amp;);</tt><tt>  A operator=(const A&amp;);</tt><tt>};</tt><tt>A f() </tt><tt>{</tt><tt>  A a;</tt><tt>  return a;</tt><tt>}</tt><tt>A a2 = f();</tt>  </pre>  The object <tt>a</tt> does not need to be copied when <tt>f()</tt> returns.   Instead, the return value of <tt>f()</tt> can be constructed directly into the   object <tt>a2</tt>, thereby avoiding both the construction and destruction of   a temporary object on the stack.   <hr></blockquote><p>Remember also that debugging and optimization are two distinct operations.   The debug version needs to be used to trap bugs and to verify that the program   is free from logical errors. The tested release version needs to be used in   performance tuning and optimizations. Of course, applying the code optimization   techniques that are presented in this chapter can enhance the performance of   the debug version as well, but the release version is the one that needs to   be used for performance evaluation.</p><blockquote>  <hr>  <strong>NOTE: </strong> It is not uncommon to find a "phantom bottleneck" in   the debug version, which the programmer strains hard to fix, only to discover   later that it has disappeared anyway in the release version. Andrew Koenig wrote   an excellent article that tells the story of an evasive bottleneck that automatically   dissolved in the release version ("An Example of Hidden Library Overhead", <i>C++   Report</i> vol. 10:2, February 1998, page 11). The lesson that can be learned   from this article is applicable to everyone who practices code optimization.   <hr></blockquote><h2> <a name="Heading4"> Declaration Placement</a></h2><p>The placing of declarations of variables and objects in the program can have   significant performance effects. Likewise, choosing between the postfix and   prefix operators can also affect performance. This section concentrates on four   issues: initialization versus assignment, relocation of declarations to the   part of the program that actually uses them, a constructor's member initialization   list, and prefix versus postfix operators.</p><h3> <a name="Heading5"> Prefer Initialization to Assignment</a></h3><p>C allows declarations only at a block's beginning, before any program statements.   For example</p><pre><tt>void f();</tt><tt>void g()</tt><tt>{</tt><tt>  int i; </tt><tt>  double d; </tt><tt>  char * p;</tt><tt>  f(); </tt><tt>}</tt></pre><p>In C++, a declaration is a statement; as such, it can appear almost anywhere   within the program. For example</p><pre><tt>void f();</tt><tt>void g()</tt><tt>{</tt><tt> int i; </tt><tt> f(); </tt><tt> double d; </tt><tt> char * p;</tt><tt>}</tt></pre><p>The motivation for this change in C++ was to allow for declarations of objects   right before they are used. There are two benefits to this practice. First,   this practice guarantees that an object cannot be tampered with by other parts   of the program before it has been used. When objects are declared at the block's   beginning and are used only 20 or 50 lines later, there is no such guarantee.   For instance, a pointer to an object that was allocated on the free store might   be accidentally deleted somewhere before it is actually used. Declaring the   pointer right before it is used, however, reduces the likelihood of such mishaps.</p><p>The second benefit in declaring objects right before their usage is the capability   to initialize them immediately with the desired value. For example</p><pre><tt>#include &lt;string&gt;</tt><tt>using namespace std;</tt><tt>void func(const string&amp; s)</tt><tt>{</tt><tt>  bool emp = s.empty(); //local declarations enables immediate initialization</tt><tt>}</tt></pre><p>For fundamental types, initialization is only marginally more efficient than   assignment; or it can be identical to late assignment in terms of performance.   Consider the following version of <tt>func()</tt>, which applies assignment 

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -