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

📄 index.html

📁 C程序员手册(英文)
💻 HTML
📖 第 1 页 / 共 5 页
字号:
<tt>}</tt>
</pre>
<p>When the program is linked, the linker is faced with two identical copies of 
  <tt>Time::Show()</tt>. Normally, function redefinition causes a link-time error. 
  Un-inlined functions are a special case, however. Older implementations of C++ 
  coped with this situation by treating an un-inlined function as if it had been 
  declared <tt>static</tt>. Consequently, each copy of the compiled function was 
  only visible within the translation unit in which it was declared. This solved 
  the name clashing problem at the cost of multiple local copies of the same function. 
  In this case, the <tt>inline</tt> declaration did not boost performance; on 
  the contrary, every call of the un-inlined function was resolved as an ordinary 
  function call with the regular overhead. Even worse, the multiple copies of 
  the function code increased compilation and linkage time and bloated the size 
  of the executable. Ironically, <i>not</i> declaring <tt>Time::Show()</tt> <tt>inline</tt> 
  might have yielded better performance! Remember that the programmer is generally 
  not aware of all the actual costs of this -- the compiler strains quietly, the 
  linker sighs silently, and the resultant executable is more bloated and sluggish 
  than ever. But it still works, and the users scratch their heads, saying, "This 
  object-oriented programming is really awful! I'm sure this app would run much 
  faster if I'd written it in C!". </p>
<p>Fortunately, the Standard's specification regarding un-inlined functions was 
  recently changed. A Standard compliant implementation generates only a single 
  copy of such a function, regardless of the number of translation units that 
  define it. In other words, an un-inlined function is treated similarly to an 
  ordinary function. However, it might take some time for all compiler vendors 
  to adopt the new specifications.</p>
<h3> <a name="Heading13"> Additional Issues of Concern</a></h3>
<p>There are two more conundrums that are associated with inline functions. The 
  first has to do with maintenance. A function can begin its life as a slim inline 
  function, offering the benefits that were previously described. At a later phase 
  in the lifetime of the system, the function body can be extended to include 
  additional functionality, resulting from changes in the implementation of its 
  class. Suddenly, the inline substitution can become inefficient or even impossible. 
  It is therefore important to reconsider the removal of the <tt>inline</tt> specifier 
  from such functions. For member functions that are defined in the class body, 
  the change is more complicated because the function definition has to be moved 
  to a separate source file.</p>
<p>Another problem might arise when inline functions are used in code libraries. 
  It is impossible to maintain binary compatibility if the definition of an inline 
  function changes. In this case, the users must recompile their code to reflect 
  the change. For a non-inline function, the users only need to relink their code, 
  which is considerably less of a burden than a massive recompilation and relink.</p>
<h3> <a name="Heading14"> The Do's and Don'ts of inline</a></h3>
<p>The lesson here is that <tt>inline</tt> is not a magical potion for enhancing 
  performance. For very short functions -- for example, accessors, mutators, and 
  function wrappers (see Chapter 13, "C Language Compatibility Issues") -- the 
  <tt>inline</tt> specifier can be profitable in terms of both execution speed 
  and program size. If the inlined function is not very short and it is called 
  extensively, however, the result can be an increase in the size of the executable. 
  Furthermore, many processors cache the machine instructions of frequently used 
  parts of the program; excessive inlining can cause a reduced instruction cache 
  hit and, consequently, poorer overall performance. The real annoyance occurs 
  when the compiler refuses to inline a function even though it was declared <tt>inline</tt>. 
  On older implementations, the result was quite painful. On Standard compliant 
  implementations, the consequences of un-inlining are less detrimental, but they 
  are still undesirable. Some compilers are clever enough to figure out on their 
  own which functions are to be inlined. However, most compilers are less inline-savvy 
  so it is best to examine the effect of an inline declaration empirically. If 
  the inline declaration does not enhance performance, avoid it. </p>
<h2> <a name="Heading15"> Optimizing Memory Usage</a></h2>
<p>Optimization has several aspects: faster execution speed, efficient usage of 
  system resources, and minimal usage of memory. In general, code optimization 
  attempts to improve all these aspects. The declaration relocation technique 
  that was demonstrated earlier eliminates the unnecessary creation and destruction 
  of objects, thereby reducing the program's size and accelerating its runtime 
  speed. However, other optimization techniques are heavily biased toward one 
  direction -- speedier code or a smaller memory footprint. Sometimes, though, 
  these goals are mutually exclusive; that is, compacting the memory footprint 
  engenders slower code, whereas a faster code implies a larger memory footprint. 
  This section presents various techniques for optimizing, or compacting, the 
  memory requirements of a program.</p>
<h3> <a name="Heading16"> Bit Fields</a></h3>
<p>In both C and C++ it is possible to store and access data directly in the tiniest 
  possible unit: a single bit. Because a bit is not the natural storage unit for 
  C/C++ implementations, the use of bit fields can increase the size of the executable 
  due to the additional maneuvers that are exercised by the processor in accessing 
  a sequence of one or more bits. This is a clear-cut case of sacrificing runtime 
  speed for the sake of minimizing memory usage.</p>
<blockquote>
  <hr>
  <strong>NOTE: </strong> Note, however, that some hardware architectures provide 
  special processor instructions for accessing bits. Therefore, whether bit fields 
  affect the program's speed or not is very much platform-dependent. 
  <hr>
</blockquote>
<p>Normally, you don't use bit fields just to save a few more bytes. For some 
  applications, however, the tradeoff between execution speed and storage compaction 
  is definitely worth its while. For example, the billing system of an average 
  international telephone company stores every phone call as a record in a relational 
  database. These records are processed in batch periodically to calculate the 
  customer's monthly bill. The database stores millions of new records every day, 
  and it has to keep the customer's billing information for at least one year. 
  The complete database contains around one billion records at any given time. 
  Because the database is also backed up periodically, and because it might also 
  be a distributed database, every record is stored in more than one physical 
  location. In fact, there might be 20 billion records stored in different backup 
  generations and distributed portions of the database at any given time. A minimal 
  billing record contains the customer's ID, a timestamp, codes that indicate 
  the type of the call (for example, local or long distance) and the tariff (off 
  peak, peak time). Literally, every bit counts here -- one redundant bit implies 
  2.5GB of wasted storage! </p>
<p>A non-space-optimizing definition of the billing record might look like this:</p>
<pre>
<tt>struct BillingRec</tt>
<tt>{</tt>
<tt>  long cust_id;</tt>
<tt>  long timestamp;</tt>
<tt>  enum CallType</tt>
<tt>  {</tt>
<tt>    toll_free,</tt>
<tt>    local,</tt>
<tt>    regional,</tt>
<tt>    long_distance,</tt>
<tt>    international,</tt>
<tt>    cellular</tt>
<tt>  } type;</tt>
<tt>  enum CallTariff</tt>
<tt>  {</tt>
<tt>    off_peak,</tt>
<tt>    medium_rate,</tt>
<tt>    peak_time</tt>
<tt>  } tariff;</tt>
<tt>};</tt>
</pre>
<p>A <tt>BillingRec</tt> occupies no fewer than 16 bytes of memory on my 32-bit 
  machine. Clearly, space is wasted here. The first two fields occupy four bytes 
  each, as expected. However, the two <tt>enum</tt> variables occupy an additional 
  eight bytes, even though they both can be safely represented in less than a 
  single byte. A tweaked version of <tt>BillingRec</tt> can squeeze the <tt>enum</tt> 
  values into two bit fields:</p>
<pre>
<tt>struct BillingRec</tt>
<tt>{</tt>
<tt>  long cust_id;</tt>
<tt>  long timestamp;</tt>
<tt>  enum CallType</tt>
<tt>  {</tt>
<tt>    toll_free,</tt>
<tt>    local,</tt>
<tt>    regional,</tt>
<tt>    long_distance,</tt>
<tt>    international,</tt>
<tt>    cellular</tt>
<tt>  };</tt>
<tt>  enum CallTariff</tt>
<tt>  {</tt>
<tt>    off_peak,</tt>
<tt>    medium_rate,</tt>
<tt>    peak_time</tt>
<tt>  };</tt>
<tt>  unsigned call: 3; //three bits </tt>
<tt>  unsigned tariff: 2; //two bits</tt>
<tt>};</tt>
</pre>
<p>The size of <tt>BillingRec</tt> is now 12 bytes. The four bytes that are saved 
  are equal to megabytes of data storage per day. Still, the size can be reduced 
  even more. The two bit fields occupy five bits in total, which is less than 
  a byte. One might therefore expect <tt>BillingRec</tt> to occupy 9 bytes rather 
  than 12. The problem is that the compiler inserts three additional padding bytes 
  after the bit fields to align the size of <tt>BillingRec</tt> on a word boundary 
  (more on member alignment in Chapter 11, "Memory<i> </i>Management"). The additional 
  padding bytes ensure faster access time -- at the cost of three wasted bytes. 
  There are two ways to overcome this problem: You can change the compiler's setting 
  to allow alignment on a byte boundary, or you can change the size of the other 
  members so that -- in total -- it reaches exactly eight bytes.</p>
<blockquote>
  <hr>
  <strong>NOTE: </strong> Note that both solutions might not be portable, and 
  on some hardware architectures, the compiler will nonetheless insist on word 
  boundary alignment. Check your compiler's specifications regarding member alignment 
  settings. 
  <hr>
</blockquote>
<p>Changing the size of the members is somewhat tricky because the first two members 
  have to become bit fields as well:</p>
<pre>
<tt>struct BillingRec</tt>
<tt>{</tt>
<tt>  int cust_id: 24; // 23 bits + 1 sign bit</tt>
<tt>  int  timestamp: 24;</tt>
<tt>  enum CallType</tt>
<tt>  {//</tt>...
<tt>  };</tt>
<tt>  enum CallTariff</tt>
<tt>  {//</tt>...
<tt>  };</tt>
<tt>  unsigned call: 3;</tt>
<tt>  unsigned tariff: 2;</tt>
<tt>};</tt>
</pre>
<p>This time, <tt>BillingRec</tt> occupies eight bytes in total, which is half 
  of its original size. The storage that is saved in this example can amount to 
  10GB annually. Considering the cheap prices of magnetic storage media these 
  days, saving a few thousand dollars might not seem to be a compelling argument 
  -- but there is another reason for favoring smaller data storage: the costs 
  of digital communication. A distributed database has synchronized copies in 
  multiple sites. The synchronization process is usually done by means of digital 
  data transfer from the central database to its synchronized copies, and vice 
  versa. The transmission of millions of records on leased lines is pretty expensive. 
  But for a phone company that owns these lines, this is not an issue of special 
  concern; suppose, however, that the company is an international bank that pays 
  hundreds of dollars for every hour of data transfer. In this case, halving the 
  data volume is unquestionably profitable. Another point to remember is the Web; 
  if the telephone company has a Web site that enables its customers to view their 
  billing information online, the download time of hundreds of records through 
  analog dialup lines can be cut in half by this tweak. </p>
<h3> <a name="Heading17"> Unions</a></h3>
<p>Unions can also be used to minimize memory waste by locating two or more data 
  members at the same memory address, where the value of (at most) one of the 
  data members is active at any time. The size of a union is sufficient to hold 
  the largest of its data members. A union can have member functions, including 
  a constructor and destructor, but it cannot have virtual member functions. A 
  union cannot serve as a base class of, nor can it inherit from, another class. 
  In addition, a union cannot store objects that have nontrivial special member 
  functions. C++ also supports <i>anonymous unions</i>. An anonymous union is 
  an unnamed object of an unnamed type (anonymous unions are also discussed in 
  Chapter 11). For example</p>
<pre>
<tt>union { long n; void * p};  // anonymous</tt>
<tt>n = 1000L;  // members are directly accessed</tt>
<tt>p = 0; // n is now also 0 </tt>
</pre>
<p>Unlike a named union, an anonymous one cannot have member functions or nonpublic 
  data members. </p>
<p>When are unions useful? The following class retrieves a person's data from 
  a database. The key can be either a unique ID number or a person's last name, 
  but never both at once:</p>
<pre>
<tt>class PersonalDetails </tt>
<tt>{</tt>
<tt>private:</tt>
<tt>  char * name;</tt>
<tt>  long ID;</tt>
<tt>  //...</tt>
<tt>public:</tt>
<tt>  PersonalDetails(const char *nm);  //key is of type char * used</tt>
<tt>  PersonalDetails(long id) : ID(id) {} //numeric key used	</tt>
<tt>};</tt>
</pre>
<p>Memory is wasted here because only one of the keys can be used at a time. An 
  anonymous union can be used in this case to minimize memory usage. For example</p>
<pre>
<tt>class PersonalDetails </tt>
<tt>{</tt>
<tt>private:</tt>
<tt>  union  //anonymous</tt>
<tt>  { </tt>
<tt>    char * name; </tt>
<tt>    long ID; </tt>

⌨️ 快捷键说明

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