abrash-black-book/01-05.html

196 lines
8.1 KiB
HTML

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta name="vsisbn" content="1576101746" />
<meta name="vstitle" content="Michael Abrash's Graphics Programming Black Book, Special Edition" />
<meta name="vsauthor" content="Michael Abrash" />
<meta name="vspublisher" content="The Coriolis Group" />
<meta name="vspubdate" content="07/01/97" />
<meta name="vscategory" content="Web and Software Development: Game Development,Web and Software Development: Graphics and Multimedia Development" />
<title>Michael Abrash's Graphics Programming Black Book Special Edition: The Best Optimizer Is between Your Ears</title>
<meta name="chapter" content="01" />
<meta name="pages" content="015-017" />
</head>
<body>
<center>
<table border="1">
<tr>
<td>
<a href="01-04.html">Previous</a>
</td>
<td>
<a href="index.html">Table of Contents</a>
</td>
<td>
<a href="01-06.html">Next</a>
</td>
</tr>
</table>
</center>
<p>The third reason is often fallacious. C library functions are not always written in assembly, nor are they always particularly well-optimized. (In fact, they&rsquo;re often written for <i>portability</i>, which has nothing to do with optimization.) What&rsquo;s more, they&rsquo;re general-purpose functions, and often can be outperformed by well-but-not- brilliantly-written code that is well-matched to a specific task. As an example, consider Listing 1.5, which uses internal buffering to handle blocks of bytes at a time. Table 1.1 shows that Listing 1.5 is 2.5 to 4 times faster than Listing 1.4 (and as much as 49 times faster than Listing 1.1!), even though it uses no assembly at all.</p>
<table width="100%">
<tr>
<td width="5%" valign="top"><img src="images/i.jpg" /></td>
<td width="95%"><small><i>Clearly, you can do well by using special-purpose C code in place of a C library function&mdash;if you have a thorough understanding of how the C library function operates and exactly what your application needs done. Otherwise, you&rsquo;ll end up rewriting C library functions in C, which makes no sense at all.</i></small></td>
</tr>
</table>
<p><b>LISTING 1.5 L1-5.C</b></p>
<pre>
/*
* Program to calculate the 16-bit checksum of the stream of bytes
* from the specified file. Buffers the bytes internally, rather
* than letting C or DOS do the work.
*/
#include &lt;stdio.h&gt;
#include &lt;fcntl.h&gt;
#include &lt;alloc.h&gt; /* alloc.h for Borland,
malloc.h for Microsoft */
#define BUFFER_SIZE 0x8000 /* 32Kb data buffer */
main(int argc, char *argv[]) {
int Handle;
unsigned int Checksum;
unsigned char *WorkingBuffer, *WorkingPtr;
int WorkingLength, LengthCount;
if ( argc != 2 ) {
printf(&ldquo;usage: checksum filename\n&rdquo;);
exit(1);
}
if ( (Handle = open(argv[1], O_RDONLY | O_BINARY)) == -1 ) {
printf(&ldquo;Can&rsquo;t open file: %s\n&rdquo;, argv[1]);
exit(1);
}
/* Get memory in which to buffer the data */
if ( (WorkingBuffer = malloc(BUFFER_SIZE)) == NULL ) {
printf(&ldquo;Can&rsquo;t get enough memory\n&rdquo;);
exit(1);
}
/* Initialize the checksum accumulator */
Checksum = 0;
/* Process the file in BUFFER_SIZE chunks */
do {
if ( (WorkingLength = read(Handle, WorkingBuffer,
BUFFER_SIZE)) == -1 ) {
printf(&ldquo;Error reading file %s\n&rdquo;, argv[1]);
exit(1);
}
/* Checksum this chunk */
WorkingPtr = WorkingBuffer;
LengthCount = WorkingLength;
while ( LengthCount-- ) {
/* Add each byte in turn into the checksum accumulator */
Checksum += (unsigned int) *WorkingPtr++;
}
} while ( WorkingLength );
/* Report the result */
printf(&ldquo;The checksum is: %u\n&rdquo;, Checksum);
exit(0);
}
</pre>
<p>That brings us to the fourth reason: avoiding an internal-buffered implementation like Listing 1.5 because of the difficulty of coding such an approach. True, it is easier to let a C library function do the work, but it&rsquo;s not all that hard to do the buffering internally. The key is the concept of handling data in <i>restartable blocks;</i> that is, reading a chunk of data, operating on the data until it runs out, suspending the operation while more data is read in, and then continuing as though nothing had happened.</p>
<p>In Listing 1.5 the restartable block implementation is pretty simple because checksumming works with one byte at a time, forgetting about each byte immediately after adding it into the total. Listing 1.5 reads in a block of bytes from the file, checksums the bytes in the block, and gets another block, repeating the process until the entire file has been processed. In Chapter 5, we&rsquo;ll see a more complex restartable block implementation, involving searching for text strings.</p>
<p>At any rate, Listing 1.5 isn&rsquo;t much more complicated than Listing 1.4&mdash;and it&rsquo;s a <i>lot</i> faster. Always consider the alternatives; a bit of clever thinking and program redesign can go a long way.</p>
<h4 id="Heading12">Know How to Turn On the Juice</h4>
<p>I have said time and again that optimization is pointless until the design is settled. When that time comes, however, optimization can indeed make a significant difference. Table 1.1 indicates that the optimized version of Listing 1.5 produced by Microsoft C outperforms an unoptimized version of the same code by more than 60 percent. What&rsquo;s more, a mostly-assembly version of Listing 1.5, shown in Listings 1.6 and 1.7, outperforms even the best-optimized C version of List1.5 by 26 percent. These are considerable improvements, well worth pursuing&mdash;once the design has been maxed out.</p>
<p><b>LISTING 1.6 L1-6.C</b></p>
<pre>
/*
* Program to calculate the 16-bit checksum of the stream of bytes
* from the specified file. Buffers the bytes internally, rather
* than letting C or DOS do the work, with the time-critical
* portion of the code written in optimized assembler.
*/
#include &lt;stdio.h&gt;
#include &lt;fcntl.h&gt;
#include &lt;alloc.h&gt; /* alloc.h for Borland,
malloc.h for Microsoft */
#define BUFFER_SIZE 0x8000 /* 32K data buffer */
main(int argc, char *argv[]) {
int Handle;
unsigned int Checksum;
unsigned char *WorkingBuffer;
int WorkingLength;
if ( argc != 2 ) {
printf(&ldquo;usage: checksum filename\n&rdquo;);
exit(1);
}
if ( (Handle = open(argv[1], O_RDONLY | O_BINARY)) == -1 ) {
printf(&ldquo;Can&rsquo;t open file: %s\n&rdquo;, argv[1]);
exit(1);
}
/* Get memory in which to buffer the data */
if ( (WorkingBuffer = malloc(BUFFER_SIZE)) == NULL ) {
printf(&ldquo;Can&rsquo;t get enough memory\n&rdquo;);
exit(1);
}
/* Initialize the checksum accumulator */
Checksum = 0;
/* Process the file in 32K chunks */
do {
if ( (WorkingLength = read(Handle, WorkingBuffer,
BUFFER_SIZE)) == -1 ) {
printf(&ldquo;Error reading file %s\n&rdquo;, argv[1]);
exit(1);
}
/* Checksum this chunk if there&rsquo;s anything in it */
if ( WorkingLength )
ChecksumChunk(WorkingBuffer, WorkingLength, &amp;Checksum);
} while ( WorkingLength );
/* Report the result */
printf(&ldquo;The checksum is: %u\n&rdquo;, Checksum);
exit(0);
}
</pre>
<center>
<table border="1">
<tr>
<td>
<a href="01-04.html">Previous</a>
</td>
<td>
<a href="index.html">Table of Contents</a>
</td>
<td>
<a href="01-06.html">Next</a>
</td>
</tr>
</table>
</center>
<hr width="90%" size="1" noshade="noshade" />
<div align="center">
Graphics Programming Black Book &copy; 2001 Michael Abrash
</div>
</body>
</html>