Strip comments and inline ids onto h*
This commit is contained in:
parent
1997a7846d
commit
0f5f617adb
362 changed files with 2519 additions and 6694 deletions
23
01-01.html
23
01-01.html
|
|
@ -10,16 +10,9 @@
|
|||
<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><!-- HEADER -->
|
||||
<!-- Empty Reference Subhead -->
|
||||
<!--ISBN=1576101746//-->
|
||||
<!--TITLE=Michael Abrash's Graphics Programming Black Book Special Edition//-->
|
||||
<!--AUTHOR=Michael Abrash//-->
|
||||
<!--PUBLISHER=The Coriolis Group, Inc.//-->
|
||||
<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="004-007" />
|
||||
<!--UNASSIGNED1//-->
|
||||
<!--UNASSIGNED2//-->
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
|
@ -39,10 +32,10 @@
|
|||
|
||||
<h2 align="center"><i>Part I</i></h2>
|
||||
|
||||
<h2><a id="Heading1"></a>Chapter 1<br />
|
||||
<h2 id="Heading1">Chapter 1<br />
|
||||
The Best Optimizer Is between Your Ears</h2>
|
||||
|
||||
<h3><a id="Heading2"></a>The Human Element of Code Optimization</h3>
|
||||
<h3 id="Heading2">The Human Element of Code Optimization</h3>
|
||||
|
||||
<p>This book is devoted to a topic near and dear to my heart: writing software that pushes PCs to the limit. Given run-of-the-mill software, PCs run like the 97-pound-weakling minicomputers they are. Give them the proper care, however, and those ugly boxes are capable of miracles. The key is this: Only on microcomputers do you have the run of the whole machine, without layers of operating systems, drivers, and the like getting in the way. You can do <i>anything</i> you want, and you can understand everything that’s going on, if you so wish.</p>
|
||||
|
||||
|
|
@ -56,7 +49,7 @@
|
|||
|
||||
<p>...now.</p>
|
||||
|
||||
<h3><a id="Heading3"></a>Understanding High Performance</h3>
|
||||
<h3 id="Heading3">Understanding High Performance</h3>
|
||||
|
||||
<p>Before we can create high-performance code, we must understand what high performance is. The objective (not always attained) in creating high-performance software is to make the software able to carry out its appointed tasks so rapidly that it responds instantaneously, as far as the user is concerned. In other words, high-performance code should ideally run so fast that any further improvement in the code would be pointless.</p>
|
||||
|
||||
|
|
@ -76,7 +69,7 @@
|
|||
|
||||
<p>“What’s a fast slow program?” you ask. That’s a good question, and a brief (true) story is perhaps the best answer.</p>
|
||||
|
||||
<h4 align="left"><a id="Heading4"></a>When Fast Isn’t Fast</h4>
|
||||
<h4 align="left" id="Heading4">When Fast Isn’t Fast</h4>
|
||||
|
||||
<p>In the early 1970s, as the first hand-held calculators were hitting the market, I knew a fellow named Irwin. He was a good student, and was planning to be an engineer. Being an engineer back then meant knowing how to use a slide rule, and Irwin could jockey a slipstick with the best of them. In fact, he was so good that he challenged a fellow with a calculator to a duel—and won, becoming a local legend in the process.</p>
|
||||
|
||||
|
|
@ -101,10 +94,6 @@
|
|||
|
||||
<div align="center">
|
||||
Graphics Programming Black Book © 2001 Michael Abrash
|
||||
</div><!-- all of the reference materials (books) have the footer and subfoot reveresed -->
|
||||
<!-- reference_subfoot = footer -->
|
||||
<!-- reference_footer = subfoot -->
|
||||
<!-- BEGIN SUB FOOTER -->
|
||||
<!-- END FOOTER -->
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
27
01-02.html
27
01-02.html
|
|
@ -10,16 +10,9 @@
|
|||
<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><!-- HEADER -->
|
||||
<!-- Empty Reference Subhead -->
|
||||
<!--ISBN=1576101746//-->
|
||||
<!--TITLE=Michael Abrash's Graphics Programming Black Book Special Edition//-->
|
||||
<!--AUTHOR=Michael Abrash//-->
|
||||
<!--PUBLISHER=The Coriolis Group, Inc.//-->
|
||||
<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="007-009" />
|
||||
<!--UNASSIGNED1//-->
|
||||
<!--UNASSIGNED2//-->
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
|
@ -37,7 +30,7 @@
|
|||
|
||||
<p><br /></p>
|
||||
|
||||
<h3><a id="Heading5"></a>Rules for Building High-Performance Code</h3>
|
||||
<h3 id="Heading5">Rules for Building High-Performance Code</h3>
|
||||
|
||||
<p>We’ve got the following rules for creating high-performance software:</p>
|
||||
|
||||
|
|
@ -59,15 +52,15 @@
|
|||
|
||||
<p>Making rules is easy; the hard part is figuring out how to apply them in the real world. For my money, examining some actual working code is always a good way to get a handle on programming concepts, so let’s look at some of the performance rules in action.</p>
|
||||
|
||||
<h4 align="left"><a id="Heading6"></a>Know Where You’re Going</h4>
|
||||
<h4 align="left" id="Heading6">Know Where You’re Going</h4>
|
||||
|
||||
<p>If we’re going to create high-performance code, first we have to know what that code is going to do. As an example, let’s write a program that generates a 16-bit checksum of the bytes in a file. In other words, the program will add each byte in a specified file in turn into a 16-bit value. This checksum value might be used to make sure that a file hasn’t been corrupted, as might occur during transmission over a modem or if a Trojan horse virus rears its ugly head. We’re not going to do anything with the checksum value other than print it out, however; right now we’re only interested in generating that checksum value as rapidly as possible.</p>
|
||||
|
||||
<h4 align="left"><a id="Heading7"></a>Make a Big Map</h4>
|
||||
<h4 align="left" id="Heading7">Make a Big Map</h4>
|
||||
|
||||
<p>How are we going to generate a checksum value for a specified file? The logical approach is to get the file name, open the file, read the bytes out of the file, add them together, and print the result. Most of those actions are straightforward; the only tricky part lies in reading the bytes and adding them together.</p>
|
||||
|
||||
<h4 align="left"><a id="Heading8"></a>Make Lots of Little Maps</h4>
|
||||
<h4 align="left" id="Heading8">Make Lots of Little Maps</h4>
|
||||
|
||||
<p>Actually, we’re only going to make one little map, because we only have one program section that requires much thought—the section that reads the bytes and adds them up. What’s the best way to do this?</p>
|
||||
|
||||
|
|
@ -79,7 +72,7 @@
|
|||
|
||||
<p>It’s <i>slow</i>.</p>
|
||||
|
||||
<p><b>LISTING 1.1 L1-1.C</b></p><!-- CODE //-->
|
||||
<p><b>LISTING 1.1 L1-1.C</b></p>
|
||||
<pre>
|
||||
/*
|
||||
* Program to calculate the 16-bit checksum of all bytes in the
|
||||
|
|
@ -121,7 +114,7 @@ main(int argc, char *argv[]) {
|
|||
printf(“The checksum is: %u\n”, Checksum);
|
||||
exit(0);
|
||||
}
|
||||
</pre><!-- END CODE //-->
|
||||
</pre>
|
||||
|
||||
<p>Table 1.1 shows the time taken for Listing 1.1 to generate a checksum of the WordPerfect version 4.2 thesaurus file, TH.WP (362,293 bytes in size), on a 10 MHz AT machine of no special parentage. Execution times are given for Listing 1.1 compiled with Borland and Microsoft compilers, with optimization both on and off; all four times are pretty much the same, however, and all are much too slow to be acceptable. Listing 1.1 requires over two and one-half minutes to checksum <i>one</i> file!</p>
|
||||
|
||||
|
|
@ -152,10 +145,6 @@ main(int argc, char *argv[]) {
|
|||
|
||||
<div align="center">
|
||||
Graphics Programming Black Book © 2001 Michael Abrash
|
||||
</div><!-- all of the reference materials (books) have the footer and subfoot reveresed -->
|
||||
<!-- reference_subfoot = footer -->
|
||||
<!-- reference_footer = subfoot -->
|
||||
<!-- BEGIN SUB FOOTER -->
|
||||
<!-- END FOOTER -->
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
25
01-03.html
25
01-03.html
|
|
@ -10,16 +10,9 @@
|
|||
<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><!-- HEADER -->
|
||||
<!-- Empty Reference Subhead -->
|
||||
<!--ISBN=1576101746//-->
|
||||
<!--TITLE=Michael Abrash's Graphics Programming Black Book Special Edition//-->
|
||||
<!--AUTHOR=Michael Abrash//-->
|
||||
<!--PUBLISHER=The Coriolis Group, Inc.//-->
|
||||
<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="010-013" />
|
||||
<!--UNASSIGNED1//-->
|
||||
<!--UNASSIGNED2//-->
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
|
@ -165,7 +158,7 @@
|
|||
</tr>
|
||||
</table>
|
||||
|
||||
<p><b>LISTING 1.2 L1-2.C</b></p><!-- CODE //-->
|
||||
<p><b>LISTING 1.2 L1-2.C</b></p>
|
||||
<pre>
|
||||
/*
|
||||
* Program to calculate the 16-bit checksum of the stream of bytes
|
||||
|
|
@ -199,9 +192,9 @@ main(int argc, char *argv[]) {
|
|||
printf(“The checksum is: %u\n”, Checksum);
|
||||
exit(0);
|
||||
}
|
||||
</pre><!-- END CODE //-->
|
||||
</pre>
|
||||
|
||||
<p><b>LISTING 1.3 L1-3.ASM</b></p><!-- CODE //-->
|
||||
<p><b>LISTING 1.3 L1-3.ASM</b></p>
|
||||
<pre>
|
||||
; Assembler subroutine to perform a 16-bit checksum on the file
|
||||
; opened on the passed-in handle. Stores the result in the
|
||||
|
|
@ -267,13 +260,13 @@ Done:
|
|||
ret
|
||||
_ChecksumFileendp
|
||||
end
|
||||
</pre><!-- END CODE //-->
|
||||
</pre>
|
||||
|
||||
<p>The lesson is clear: Optimization makes code faster, but without proper design, optimization just creates fast slow code.</p>
|
||||
|
||||
<p>Well, then, how are we going to improve our design? Before we can do that, we have to understand what’s wrong with the current design.</p>
|
||||
|
||||
<h4 align="left"><a id="Heading9"></a>Know the Territory</h4>
|
||||
<h4 align="left" id="Heading9">Know the Territory</h4>
|
||||
|
||||
<p>Just why is Listing 1.1 so slow? In a word: overhead. The C library implements the <b>read()</b> function by calling DOS to read the desired number of bytes. (I figured this out by watching the code execute with a debugger, but you can buy library source code from both Microsoft and Borland.) That means that Listing 1.1 (and Listing 1.3 as well) executes one DOS function per byte processed—and DOS functions, especially this one, come with a lot of overhead.</p>
|
||||
|
||||
|
|
@ -302,10 +295,6 @@ _ChecksumFileendp
|
|||
|
||||
<div align="center">
|
||||
Graphics Programming Black Book © 2001 Michael Abrash
|
||||
</div><!-- all of the reference materials (books) have the footer and subfoot reveresed -->
|
||||
<!-- reference_subfoot = footer -->
|
||||
<!-- reference_footer = subfoot -->
|
||||
<!-- BEGIN SUB FOOTER -->
|
||||
<!-- END FOOTER -->
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
23
01-04.html
23
01-04.html
|
|
@ -10,16 +10,9 @@
|
|||
<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><!-- HEADER -->
|
||||
<!-- Empty Reference Subhead -->
|
||||
<!--ISBN=1576101746//-->
|
||||
<!--TITLE=Michael Abrash's Graphics Programming Black Book Special Edition//-->
|
||||
<!--AUTHOR=Michael Abrash//-->
|
||||
<!--PUBLISHER=The Coriolis Group, Inc.//-->
|
||||
<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="013-015" />
|
||||
<!--UNASSIGNED1//-->
|
||||
<!--UNASSIGNED2//-->
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
|
@ -47,7 +40,7 @@
|
|||
|
||||
<p>In this case that means knowing how DOS and the C/C<small>++</small> file-access libraries do their work. In other words, <i>know the territory</i>!</p>
|
||||
|
||||
<p><b>LISTING 1.4 L1-4.C</b></p><!-- CODE //-->
|
||||
<p><b>LISTING 1.4 L1-4.C</b></p>
|
||||
<pre>
|
||||
/*
|
||||
* Program to calculate the 16-bit checksum of the stream of bytes
|
||||
|
|
@ -82,9 +75,9 @@ main(int argc, char *argv[]) {
|
|||
printf(“The checksum is: %u\n”, Checksum);
|
||||
exit(0);
|
||||
}
|
||||
</pre><!-- END CODE //-->
|
||||
</pre>
|
||||
|
||||
<h4 align="left"><a id="Heading10"></a>Know When It Matters</h4>
|
||||
<h4 align="left" id="Heading10">Know When It Matters</h4>
|
||||
|
||||
<p>The last section contained a particularly interesting phrase: <i>the time-critical portions of your code</i>. Time-critical portions of your code are those portions in which the speed of the code makes a significant difference in the overall performance of your program—and by “significant,” I don’t mean that it makes the code 100 percent faster, or 200 percent, or any particular amount at all, but rather that it makes the program more responsive and/or usable <i>from the user’s perspective</i>.</p>
|
||||
|
||||
|
|
@ -100,7 +93,7 @@ main(int argc, char *argv[]) {
|
|||
|
||||
<p>Besides, we don’t want to optimize until the design is refined to our satisfaction, and that won’t be the case until we’ve thought about other approaches.</p>
|
||||
|
||||
<h4 align="left"><a id="Heading11"></a>Always Consider the Alternatives</h4>
|
||||
<h4 align="left" id="Heading11">Always Consider the Alternatives</h4>
|
||||
|
||||
<p>Listing 1.4 is good, but let’s see if there are other—perhaps less obvious—ways to get the same results faster. Let’s start by considering why Listing 1.4 is so much better than Listing 1.1. Like <b>read()</b>, <b>getc()</b> calls DOS to read from the file; the speed improvement of Listing 1.4 over Listing 1.1 occurs because <b>getc()</b> eads many bytes at once via DOS, then manages those bytes for us. That’s faster than reading them one at a time using <b>read()</b>—but there’s no reason to think that it’s faster than having our program read and manage blocks itself. Easier, yes, but not faster.</p>
|
||||
|
||||
|
|
@ -139,10 +132,6 @@ main(int argc, char *argv[]) {
|
|||
|
||||
<div align="center">
|
||||
Graphics Programming Black Book © 2001 Michael Abrash
|
||||
</div><!-- all of the reference materials (books) have the footer and subfoot reveresed -->
|
||||
<!-- reference_subfoot = footer -->
|
||||
<!-- reference_footer = subfoot -->
|
||||
<!-- BEGIN SUB FOOTER -->
|
||||
<!-- END FOOTER -->
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
25
01-05.html
25
01-05.html
|
|
@ -10,16 +10,9 @@
|
|||
<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><!-- HEADER -->
|
||||
<!-- Empty Reference Subhead -->
|
||||
<!--ISBN=1576101746//-->
|
||||
<!--TITLE=Michael Abrash's Graphics Programming Black Book Special Edition//-->
|
||||
<!--AUTHOR=Michael Abrash//-->
|
||||
<!--PUBLISHER=The Coriolis Group, Inc.//-->
|
||||
<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" />
|
||||
<!--UNASSIGNED1//-->
|
||||
<!--UNASSIGNED2//-->
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
|
@ -47,7 +40,7 @@
|
|||
</tr>
|
||||
</table>
|
||||
|
||||
<p><b>LISTING 1.5 L1-5.C</b></p><!-- CODE //-->
|
||||
<p><b>LISTING 1.5 L1-5.C</b></p>
|
||||
<pre>
|
||||
/*
|
||||
* Program to calculate the 16-bit checksum of the stream of bytes
|
||||
|
|
@ -105,7 +98,7 @@ main(int argc, char *argv[]) {
|
|||
printf(“The checksum is: %u\n”, Checksum);
|
||||
exit(0);
|
||||
}
|
||||
</pre><!-- END CODE //-->
|
||||
</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’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>
|
||||
|
||||
|
|
@ -113,11 +106,11 @@ main(int argc, char *argv[]) {
|
|||
|
||||
<p>At any rate, Listing 1.5 isn’t much more complicated than Listing 1.4—and it’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 align="left"><a id="Heading12"></a>Know How to Turn On the Juice</h4>
|
||||
<h4 align="left" 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’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—once the design has been maxed out.</p>
|
||||
|
||||
<p><b>LISTING 1.6 L1-6.C</b></p><!-- CODE //-->
|
||||
<p><b>LISTING 1.6 L1-6.C</b></p>
|
||||
<pre>
|
||||
/*
|
||||
* Program to calculate the 16-bit checksum of the stream of bytes
|
||||
|
|
@ -172,7 +165,7 @@ main(int argc, char *argv[]) {
|
|||
printf(“The checksum is: %u\n”, Checksum);
|
||||
exit(0);
|
||||
}
|
||||
</pre><!-- END CODE //-->
|
||||
</pre>
|
||||
|
||||
<p><br /></p>
|
||||
|
||||
|
|
@ -191,10 +184,6 @@ main(int argc, char *argv[]) {
|
|||
|
||||
<div align="center">
|
||||
Graphics Programming Black Book © 2001 Michael Abrash
|
||||
</div><!-- all of the reference materials (books) have the footer and subfoot reveresed -->
|
||||
<!-- reference_subfoot = footer -->
|
||||
<!-- reference_footer = subfoot -->
|
||||
<!-- BEGIN SUB FOOTER -->
|
||||
<!-- END FOOTER -->
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
23
01-06.html
23
01-06.html
|
|
@ -10,16 +10,9 @@
|
|||
<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><!-- HEADER -->
|
||||
<!-- Empty Reference Subhead -->
|
||||
<!--ISBN=1576101746//-->
|
||||
<!--TITLE=Michael Abrash's Graphics Programming Black Book Special Edition//-->
|
||||
<!--AUTHOR=Michael Abrash//-->
|
||||
<!--PUBLISHER=The Coriolis Group, Inc.//-->
|
||||
<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="018-019" />
|
||||
<!--UNASSIGNED1//-->
|
||||
<!--UNASSIGNED2//-->
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
|
@ -37,7 +30,7 @@
|
|||
|
||||
<p><br /></p>
|
||||
|
||||
<p><b>LISTING 1.7 L1-7.ASM</b></p><!-- CODE //-->
|
||||
<p><b>LISTING 1.7 L1-7.ASM</b></p>
|
||||
<pre>
|
||||
; Assembler subroutine to perform a 16-bit checksum on a block of
|
||||
; bytes 1 to 64K in size. Adds checksum for block into passed-in
|
||||
|
|
@ -88,7 +81,7 @@ ChecksumLoop:
|
|||
ret
|
||||
_ChecksumChunkendp
|
||||
end
|
||||
</pre><!-- END CODE //-->
|
||||
</pre>
|
||||
|
||||
<p>Note that in Table 1.1, optimization makes little difference except in the case of Listing 1.5, where the design has been refined considerably. Execution time in the other cases is dominated by time spent in DOS and/or the C library, so optimization of the code you write is pretty much irrelevant. What’s more, while the approximately two-times improvement we got by optimizing is not to be sneezed at, it pales against the up-to-50-times improvement we got by redesigning.</p>
|
||||
|
||||
|
|
@ -104,13 +97,13 @@ _ChecksumChunkendp
|
|||
|
||||
<p>All this is basically a way of saying: Know where you’re going, know the territory, and know when it matters.</p>
|
||||
|
||||
<h3><a id="Heading13"></a>Where We’ve Been, What We’ve Seen</h3>
|
||||
<h3 id="Heading13">Where We’ve Been, What We’ve Seen</h3>
|
||||
|
||||
<p>What have we learned? Don’t let other people’s code—even DOS—do the work for you when speed matters, at least not without knowing what that code does and how well it performs.</p>
|
||||
|
||||
<p>Optimization only matters after you’ve done your part on the program design end. Consider the ratios on the vertical axis of Table 1.1, which show that optimization is almost totally wasted in the checksumming application without an efficient design. Optimization is no panacea. Table 1.1 shows a two-times improvement from optimization—and a 50-times-plus improvement from redesign. The longstanding debate about which C compiler optimizes code best doesn’t matter quite so much in light of Table 1.1, does it? Your organic optimizer matters much more than your compiler’s optimizer, and there’s always assembly for those usually small sections of code where performance really matters.</p>
|
||||
|
||||
<h4 align="left"><a id="Heading14"></a>Where We’re Going</h4>
|
||||
<h4 align="left" id="Heading14">Where We’re Going</h4>
|
||||
|
||||
<p>This chapter has presented a quick step-by-step overview of the design process. I’m not claiming that this is the only way to create high-performance code; it’s just an approach that works for me. Create code however you want, but never forget that design matters more than detailed optimization. Never stop looking for inventive ways to boost performance—and never waste time speeding up code that doesn’t need to be sped up.</p>
|
||||
|
||||
|
|
@ -133,10 +126,6 @@ _ChecksumChunkendp
|
|||
|
||||
<div align="center">
|
||||
Graphics Programming Black Book © 2001 Michael Abrash
|
||||
</div><!-- all of the reference materials (books) have the footer and subfoot reveresed -->
|
||||
<!-- reference_subfoot = footer -->
|
||||
<!-- reference_footer = subfoot -->
|
||||
<!-- BEGIN SUB FOOTER -->
|
||||
<!-- END FOOTER -->
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
31
02-01.html
31
02-01.html
|
|
@ -10,16 +10,9 @@
|
|||
<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: A World Apart</title><!-- HEADER -->
|
||||
<!-- Empty Reference Subhead -->
|
||||
<!--ISBN=1576101746//-->
|
||||
<!--TITLE=Michael Abrash's Graphics Programming Black Book Special Edition//-->
|
||||
<!--AUTHOR=Michael Abrash//-->
|
||||
<!--PUBLISHER=The Coriolis Group, Inc.//-->
|
||||
<title>Michael Abrash's Graphics Programming Black Book Special Edition: A World Apart</title>
|
||||
<meta name="chapter" content="02" />
|
||||
<meta name="pages" content="021-025" />
|
||||
<!--UNASSIGNED1//-->
|
||||
<!--UNASSIGNED2//-->
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
|
@ -37,10 +30,10 @@
|
|||
|
||||
<p><br /></p>
|
||||
|
||||
<h2><a id="Heading1"></a>Chapter 2<br />
|
||||
<h2 id="Heading1">Chapter 2<br />
|
||||
A World Apart</h2>
|
||||
|
||||
<h3><a id="Heading2"></a>The Unique Nature of Assembly Language Optimization</h3>
|
||||
<h3 id="Heading2">The Unique Nature of Assembly Language Optimization</h3>
|
||||
|
||||
<p>As I showed in the previous chapter, optimization is by no means always a matter of “dropping into assembly.” In fact, in performance tuning high-level language code, assembly should be used rarely, and then only after you’ve made sure a badly chosen or clumsily implemented algorithm isn’t eating you alive. Certainly if you use assembly at all, make absolutely sure you use it <i>right</i>. The potential of assembly code to run <i>slowly</i> is poorly understood by a lot of people, but that potential is great, especially in the hands of the ignorant.</p>
|
||||
|
||||
|
|
@ -48,9 +41,9 @@
|
|||
|
||||
<p>As usual, the best way to wade in is to present a real-world example.</p>
|
||||
|
||||
<h3><a id="Heading3"></a>Instructions: The Individual versus the Collective</h3>
|
||||
<h3 id="Heading3">Instructions: The Individual versus the Collective</h3>
|
||||
|
||||
<p>Some time ago, I was asked to work over a critical assembly subroutine in order to make it run as fast as possible. The task of the subroutine was to construct a nibble out of four bits read from different bytes, rotating and combining the bits so that they ultimately ended up neatly aligned in bits 3-0 of a single byte. (In case you’re curious, the object was to construct a 16-color pixel from bits scattered over 4 bytes.) I examined the subroutine line by line, saving a cycle here and a cycle there, until the code truly seemed to be optimized. When I was done, the key part of the code looked something like this:</p><!-- CODE SNIP //-->
|
||||
<p>Some time ago, I was asked to work over a critical assembly subroutine in order to make it run as fast as possible. The task of the subroutine was to construct a nibble out of four bits read from different bytes, rotating and combining the bits so that they ultimately ended up neatly aligned in bits 3-0 of a single byte. (In case you’re curious, the object was to construct a 16-color pixel from bits scattered over 4 bytes.) I examined the subroutine line by line, saving a cycle here and a cycle there, until the code truly seemed to be optimized. When I was done, the key part of the code looked something like this:</p>
|
||||
<pre>
|
||||
LoopTop:
|
||||
lodsb ;get the next byte to extract a bit from
|
||||
|
|
@ -60,7 +53,7 @@ LoopTop:
|
|||
dec cx ;the next bit goes 1 place to the right
|
||||
dec dx ;count down the number of bits
|
||||
jnz LoopTop ;process the next bit, if any
|
||||
</pre><!-- END CODE SNIP //-->
|
||||
</pre>
|
||||
|
||||
<p>Now, it’s hard to write code that’s much faster than seven instructions, only one of which accesses memory, and most programmers would have called it a day at this point. Still, something bothered me, so I spent a bit of time going over the code again. Suddenly, the answer struck me—the code was rotating each bit into place separately, so that a multibit rotation was being performed every time through the loop, for a total of four separate time-consuming multibit rotations!</p>
|
||||
|
||||
|
|
@ -72,7 +65,7 @@ LoopTop:
|
|||
</tr>
|
||||
</table>
|
||||
|
||||
<p>I changed the code to the following:</p><!-- CODE //-->
|
||||
<p>I changed the code to the following:</p>
|
||||
<pre>
|
||||
LoopTop:
|
||||
lodsb ;get the next byte to extract a bit from
|
||||
|
|
@ -83,13 +76,13 @@ LoopTop:
|
|||
jnz LoopTop ;process the next bit, if any
|
||||
rol bl,cl ;rotate all four bits into their final
|
||||
; positions at the same time
|
||||
</pre><!-- END CODE //-->
|
||||
</pre>
|
||||
|
||||
<p>This moved the costly multibit rotation out of the loop so that it was performed just once, rather than four times. While the code may not look much different from the original, and in fact still contains exactly the same number of instructions, the performance of the entire subroutine improved by about 10 percent from just this one change. (Incidentally, that wasn’t the end of the optimization; I eliminated the <b>DEC</b> and <b>JNJ</b> instructions by expanding the four iterations of the loop—but that’s a tale for another chapter.)</p>
|
||||
|
||||
<p>The point is this: To write truly superior assembly programs, you need to know what the various instructions do and which instructions execute fastest...and more. You must also learn to look at your programming problems from a variety of perspectives so that you can put those fast instructions to work in the most effective ways.</p>
|
||||
|
||||
<h3><a id="Heading4"></a>Assembly Is Fundamentally Different</h3>
|
||||
<h3 id="Heading4">Assembly Is Fundamentally Different</h3>
|
||||
|
||||
<p>Is it really so hard as all that to write good assembly code for the PC? Yes! Thanks to the decidedly quirky nature of the x86 family CPUs, assembly language differs fundamentally from other languages, and is undeniably harder to work with. On the other hand, the potential of assembly code is much greater than that of other languages, as well.</p>
|
||||
|
||||
|
|
@ -112,10 +105,6 @@ LoopTop:
|
|||
|
||||
<div align="center">
|
||||
Graphics Programming Black Book © 2001 Michael Abrash
|
||||
</div><!-- all of the reference materials (books) have the footer and subfoot reveresed -->
|
||||
<!-- reference_subfoot = footer -->
|
||||
<!-- reference_footer = subfoot -->
|
||||
<!-- BEGIN SUB FOOTER -->
|
||||
<!-- END FOOTER -->
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
31
02-02.html
31
02-02.html
|
|
@ -10,16 +10,9 @@
|
|||
<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: A World Apart</title><!-- HEADER -->
|
||||
<!-- Empty Reference Subhead -->
|
||||
<!--ISBN=1576101746//-->
|
||||
<!--TITLE=Michael Abrash's Graphics Programming Black Book Special Edition//-->
|
||||
<!--AUTHOR=Michael Abrash//-->
|
||||
<!--PUBLISHER=The Coriolis Group, Inc.//-->
|
||||
<title>Michael Abrash's Graphics Programming Black Book Special Edition: A World Apart</title>
|
||||
<meta name="chapter" content="02" />
|
||||
<meta name="pages" content="025-028" />
|
||||
<!--UNASSIGNED1//-->
|
||||
<!--UNASSIGNED2//-->
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
|
@ -37,7 +30,7 @@
|
|||
|
||||
<p><br /></p>
|
||||
|
||||
<h4 align="left"><a id="Heading5"></a>Transformation Inefficiencies</h4>
|
||||
<h4 align="left" id="Heading5">Transformation Inefficiencies</h4>
|
||||
|
||||
<p>No matter how well an implementation is derived from the corresponding design, however, high-level languages like C/C<small>++</small> and Pascal inevitably introduce additional transformation inefficiencies, as shown in Figure 2.1.</p>
|
||||
|
||||
|
|
@ -45,25 +38,23 @@
|
|||
|
||||
<p>High-level languages provide artificial environments that lend themselves relatively well to human programming skills, in order to ease the transition from design to implementation. The price for this ease of implementation is a considerable loss of efficiency in transforming source code into machine language. This is particularly true given that the x86 family in real and 16-bit protected mode, with its specialized memory-addressing instructions and segmented memory architecture, does not lend itself particularly well to compiler design. Even the 32-bit mode of the 386 and its successors, with their more powerful addressing modes, offer fewer registers than compilers would like.</p>
|
||||
|
||||
<p><a id="Fig1"><!-- </A><A HREF="javascript:displayWindow('images/02-01.jpg',417,202 )"> --><img src="images/02-01.jpg" /><br />
|
||||
<!-- </A>
|
||||
<BR><A HREF="javascript:displayWindow('images/02-01.jpg',417,202)"> --><b>Figure 2.1</b></a> <i>The high-level language transformation inefficiencies.</i></p>
|
||||
<p><a id="Fig1"><img src="images/02-01.jpg" /><br />
|
||||
<b>Figure 2.1</b></a> <i>The high-level language transformation inefficiencies.</i></p>
|
||||
|
||||
<p>Assembly, on the other hand, is simply a human-oriented representation of machine language. As a result, assembly provides a difficult programming environment—the bare hardware and systems software of the computer—<i>but properly constructed assembly programs suffer no transformation loss</i>, as shown in Figure 2.2.</p>
|
||||
|
||||
<p>Only one transformation is required when creating an assembler program, and that single transformation is completely under the programmer’s control. Assemblers perform no transformation from source code to machine language; instead, they merely map assembler instructions to machine language instructions on a one-to-one basis. As a result, the programmer is able to produce machine language code that’s precisely tailored to the needs of each task a given application requires.</p>
|
||||
|
||||
<p><a id="Fig2"><!-- </A><A HREF="javascript:displayWindow('images/02-02.jpg',414,201 )"> --><img src="images/02-02.jpg" /><br />
|
||||
<!-- </A>
|
||||
<BR><A HREF="javascript:displayWindow('images/02-02.jpg',414,201)"> --><b>Figure 2.2</b></a> <i>Properly constructed assembly programs suffer no transformation loss.</i></p>
|
||||
<p><a id="Fig2"><img src="images/02-02.jpg" /><br />
|
||||
<b>Figure 2.2</b></a> <i>Properly constructed assembly programs suffer no transformation loss.</i></p>
|
||||
|
||||
<p>The key, of course, is the programmer, since in assembly the programmer must essentially perform the transformation from the application specification to machine language entirely on his or her own. (The assembler merely handles the <i>direct</i> translation from assembly to machine language.)</p>
|
||||
|
||||
<h4 align="left"><a id="Heading6"></a>Self-Reliance</h4>
|
||||
<h4 align="left" id="Heading6">Self-Reliance</h4>
|
||||
|
||||
<p>The first part of assembly language optimization, then, is self. An assembler is nothing more than a tool to let you design machine-language programs without having to think in hexadecimal codes. So assembly language programmers—unlike all other programmers—must take full responsibility for the quality of their code. Since assemblers provide little help at any level higher than the generation of machine language, the assembly programmer must be capable both of coding any programming construct directly and of controlling the PC at the lowest practical level—the operating system, the BIOS, even the hardware where necessary. High-level languages handle most of this transparently to the programmer, but in assembly everything is fair—and necessary—game, which brings us to another aspect of assembly optimization: knowledge.</p>
|
||||
|
||||
<h4 align="left"><a id="Heading7"></a>Knowledge</h4>
|
||||
<h4 align="left" id="Heading7">Knowledge</h4>
|
||||
|
||||
<p>In the PC world, you can never have enough knowledge, and every item you add to your store will make your programs better. Thorough familiarity with both the operating system APIs and BIOS interfaces is important; since those interfaces are well-documented and reasonably straightforward, my advice is to get a good book or two and bring yourself up to speed. Similarly, familiarity with the PC hardware is required. While that topic covers a lot of ground—display adapters, keyboards, serial ports, printer ports, timer and DMA channels, memory organization, and more—most of the hardware is well-documented, and articles about programming major hardware components appear frequently in the literature, so this sort of knowledge can be acquired readily enough.</p>
|
||||
|
||||
|
|
@ -94,10 +85,6 @@
|
|||
|
||||
<div align="center">
|
||||
Graphics Programming Black Book © 2001 Michael Abrash
|
||||
</div><!-- all of the reference materials (books) have the footer and subfoot reveresed -->
|
||||
<!-- reference_subfoot = footer -->
|
||||
<!-- reference_footer = subfoot -->
|
||||
<!-- BEGIN SUB FOOTER -->
|
||||
<!-- END FOOTER -->
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
19
02-03.html
19
02-03.html
|
|
@ -10,16 +10,9 @@
|
|||
<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: A World Apart</title><!-- HEADER -->
|
||||
<!-- Empty Reference Subhead -->
|
||||
<!--ISBN=1576101746//-->
|
||||
<!--TITLE=Michael Abrash's Graphics Programming Black Book Special Edition//-->
|
||||
<!--AUTHOR=Michael Abrash//-->
|
||||
<!--PUBLISHER=The Coriolis Group, Inc.//-->
|
||||
<title>Michael Abrash's Graphics Programming Black Book Special Edition: A World Apart</title>
|
||||
<meta name="chapter" content="02" />
|
||||
<meta name="pages" content="028-030" />
|
||||
<!--UNASSIGNED1//-->
|
||||
<!--UNASSIGNED2//-->
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
|
@ -37,7 +30,7 @@
|
|||
|
||||
<p><br /></p>
|
||||
|
||||
<h3><a id="Heading8"></a>The Flexible Mind</h3>
|
||||
<h3 id="Heading8">The Flexible Mind</h3>
|
||||
|
||||
<p>Is the never-ending collection of information all there is to the assembly optimization, then? Hardly. Knowledge is simply a necessary base on which to build. Let’s take a moment to examine the objectives of good assembly programming, and the remainder of the forces that act on assembly optimization will fall into place.</p>
|
||||
|
||||
|
|
@ -65,7 +58,7 @@
|
|||
|
||||
<p>The gist of all this is simply that good assembly programming is done in the context of a solid overall framework unique to each program, and the flexible mind is the key to creating that framework and holding it together.</p>
|
||||
|
||||
<h4 align="left"><a id="Heading9"></a>Where to Begin?</h4>
|
||||
<h4 align="left" id="Heading9">Where to Begin?</h4>
|
||||
|
||||
<p>To summarize, the skill of assembly language optimization is a combination of knowledge, perspective, and a way of thought that makes possible the genesis of absolutely the fastest or the smallest code. With that in mind, what should the first step be? Development of the flexible mind is an obvious step. Still, the flexible mind is no better than the knowledge at its disposal. The first step in the journey toward mastering optimization at that exalted level, then, would seem to be learning how to learn.</p>
|
||||
|
||||
|
|
@ -86,10 +79,6 @@
|
|||
|
||||
<div align="center">
|
||||
Graphics Programming Black Book © 2001 Michael Abrash
|
||||
</div><!-- all of the reference materials (books) have the footer and subfoot reveresed -->
|
||||
<!-- reference_subfoot = footer -->
|
||||
<!-- reference_footer = subfoot -->
|
||||
<!-- BEGIN SUB FOOTER -->
|
||||
<!-- END FOOTER -->
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
23
03-01.html
23
03-01.html
|
|
@ -10,16 +10,9 @@
|
|||
<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: Assume Nothing</title><!-- HEADER -->
|
||||
<!-- Empty Reference Subhead -->
|
||||
<!--ISBN=1576101746//-->
|
||||
<!--TITLE=Michael Abrash's Graphics Programming Black Book Special Edition//-->
|
||||
<!--AUTHOR=Michael Abrash//-->
|
||||
<!--PUBLISHER=The Coriolis Group, Inc.//-->
|
||||
<title>Michael Abrash's Graphics Programming Black Book Special Edition: Assume Nothing</title>
|
||||
<meta name="chapter" content="03" />
|
||||
<meta name="pages" content="031-035" />
|
||||
<!--UNASSIGNED1//-->
|
||||
<!--UNASSIGNED2//-->
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
|
@ -37,10 +30,10 @@
|
|||
|
||||
<p><br /></p>
|
||||
|
||||
<h2><a id="Heading1"></a>Chapter 3<br />
|
||||
<h2 id="Heading1">Chapter 3<br />
|
||||
Assume Nothing</h2>
|
||||
|
||||
<h3><a id="Heading2"></a>Understanding and Using the Zen Timer</h3>
|
||||
<h3 id="Heading2">Understanding and Using the Zen Timer</h3>
|
||||
|
||||
<p>When you’re pushing the envelope in writing optimized PC code, you’re likely to become more than a little compulsive about finding approaches that let you wring more speed from your computer. In the process, you’re bound to make mistakes, which is fine—as long as you watch for those mistakes and <i>learn</i> from them.</p>
|
||||
|
||||
|
|
@ -50,7 +43,7 @@
|
|||
|
||||
<p>It ran slower than the original version!</p>
|
||||
|
||||
<h3><a id="Heading3"></a>The Costs of Ignorance</h3>
|
||||
<h3 id="Heading3">The Costs of Ignorance</h3>
|
||||
|
||||
<p>As diligent as the author had been, he had nonetheless committed a cardinal sin of x86 assembly language programming: He had assumed that the information available to him was both correct and complete. While the execution times provided by Intel for its processors are indeed correct, they are incomplete; the other—and often more important—part of code performance is instruction <i>fetch</i> time, a topic to which I will return in later chapters.</p>
|
||||
|
||||
|
|
@ -70,7 +63,7 @@
|
|||
|
||||
<p>Ignorance can also be responsible for considerable wasted effort. I recall a debate in the letters column of one computer magazine about exactly how quickly text can be drawn on a Color/Graphics Adapter (CGA) screen without causing snow. The letter-writers counted every cycle in their timing loops, just as the author in the story that started this chapter had. Like that author, the letter-writers had failed to take the prefetch queue into account. In fact, they had neglected the effects of video wait states as well, so the code they discussed was actually <i>much</i> slower than their estimates. The proper test would, of course, have been to run the code to see if snow resulted, since the only true measure of code performance is observing it in action.</p>
|
||||
|
||||
<h3><a id="Heading4"></a>The Zen Timer</h3>
|
||||
<h3 id="Heading4">The Zen Timer</h3>
|
||||
|
||||
<p>Clearly, one key to mastering Zen-class optimization is a tool with which to measure code performance. The most accurate way to measure performance is with expensive hardware, but reasonable measurements at no cost can be made with the PC’s 8253 timer chip, which counts at a rate of slightly over 1,000,000 times per second. The 8253 can be started at the beginning of a block of code of interest and stopped at the end of that code, with the resulting count indicating how long the code took to execute with an accuracy of about 1 microsecond. (A microsecond is one millionth of a second, and is abbreviated µs). To be precise, the 8253 counts once every 838.1 nanoseconds. (A nanosecond is one billionth of a second, and is abbreviated ns.)</p>
|
||||
|
||||
|
|
@ -93,10 +86,6 @@
|
|||
|
||||
<div align="center">
|
||||
Graphics Programming Black Book © 2001 Michael Abrash
|
||||
</div><!-- all of the reference materials (books) have the footer and subfoot reveresed -->
|
||||
<!-- reference_subfoot = footer -->
|
||||
<!-- reference_footer = subfoot -->
|
||||
<!-- BEGIN SUB FOOTER -->
|
||||
<!-- END FOOTER -->
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
19
03-02.html
19
03-02.html
|
|
@ -10,16 +10,9 @@
|
|||
<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: Assume Nothing</title><!-- HEADER -->
|
||||
<!-- Empty Reference Subhead -->
|
||||
<!--ISBN=1576101746//-->
|
||||
<!--TITLE=Michael Abrash's Graphics Programming Black Book Special Edition//-->
|
||||
<!--AUTHOR=Michael Abrash//-->
|
||||
<!--PUBLISHER=The Coriolis Group, Inc.//-->
|
||||
<title>Michael Abrash's Graphics Programming Black Book Special Edition: Assume Nothing</title>
|
||||
<meta name="chapter" content="03" />
|
||||
<meta name="pages" content="035-042" />
|
||||
<!--UNASSIGNED1//-->
|
||||
<!--UNASSIGNED2//-->
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
|
@ -37,7 +30,7 @@
|
|||
|
||||
<p><br /></p>
|
||||
|
||||
<p><b>LISTING 3.1 PZTIMER.ASM</b></p><!-- CODE //-->
|
||||
<p><b>LISTING 3.1 PZTIMER.ASM</b></p>
|
||||
<pre>
|
||||
; The precision Zen timer (PZTIMER.ASM)
|
||||
;
|
||||
|
|
@ -478,7 +471,7 @@ ZTimerReport endp
|
|||
|
||||
Code ends
|
||||
end
|
||||
</pre><!-- END CODE //-->
|
||||
</pre>
|
||||
|
||||
<p><br /></p>
|
||||
|
||||
|
|
@ -497,10 +490,6 @@ Code ends
|
|||
|
||||
<div align="center">
|
||||
Graphics Programming Black Book © 2001 Michael Abrash
|
||||
</div><!-- all of the reference materials (books) have the footer and subfoot reveresed -->
|
||||
<!-- reference_subfoot = footer -->
|
||||
<!-- reference_footer = subfoot -->
|
||||
<!-- BEGIN SUB FOOTER -->
|
||||
<!-- END FOOTER -->
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
26
03-03.html
26
03-03.html
|
|
@ -10,16 +10,9 @@
|
|||
<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: Assume Nothing</title><!-- HEADER -->
|
||||
<!-- Empty Reference Subhead -->
|
||||
<!--ISBN=1576101746//-->
|
||||
<!--TITLE=Michael Abrash's Graphics Programming Black Book Special Edition//-->
|
||||
<!--AUTHOR=Michael Abrash//-->
|
||||
<!--PUBLISHER=The Coriolis Group, Inc.//-->
|
||||
<title>Michael Abrash's Graphics Programming Black Book Special Edition: Assume Nothing</title>
|
||||
<meta name="chapter" content="03" />
|
||||
<meta name="pages" content="042-045" />
|
||||
<!--UNASSIGNED1//-->
|
||||
<!--UNASSIGNED2//-->
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
|
@ -37,19 +30,19 @@
|
|||
|
||||
<p><br /></p>
|
||||
|
||||
<h4 align="left"><a id="Heading5"></a>The Zen Timer Is a Means, Not an End</h4>
|
||||
<h4 align="left" id="Heading5">The Zen Timer Is a Means, Not an End</h4>
|
||||
|
||||
<p>We’re going to spend the rest of this chapter seeing what the Zen timer can do, examining how it works, and learning how to use it. I’ll be using the Zen timer again and again over the course of this book, so it’s essential that you learn what the Zen timer can do and how to use it. On the other hand, it is by no means essential that you understand exactly how the Zen timer works. (Interesting, yes; essential, no.)</p>
|
||||
|
||||
<p>In other words, the Zen timer isn’t really part of the knowledge we seek; rather, it’s one tool with which we’ll acquire that knowledge. Consequently, you shouldn’t worry if you don’t fully grasp the inner workings of the Zen timer. Instead, focus on learning how to <i>use</i> it, and you’ll be on the right road.</p>
|
||||
|
||||
<h4 align="left"><a id="Heading6"></a>Starting the Zen Timer</h4>
|
||||
<h4 align="left" id="Heading6">Starting the Zen Timer</h4>
|
||||
|
||||
<p><b>ZTimerOn</b> is called at the start of a segment of code to be timed. <b>ZTimerOn</b> saves the context of the calling code, disables interrupts, sets timer 0 of the 8253 to mode 2 (divide-by-N mode), sets the initial timer count to 0, restores the context of the calling code, and returns. (I’d like to note that while Intel’s documentation for the 8253 seems to indicate that a timer won’t reset to 0 until it finishes counting down, in actual practice, timers seem to reset to 0 as soon as they’re loaded.)</p>
|
||||
|
||||
<p>Two aspects of <b>ZTimerOn</b> are worth discussing further. One point of interest is that <b>ZTimerOn</b> disables interrupts. (<b>ZTimerOff</b> later restores interrupts to the state they were in when <b>ZTimerOn</b> was called.) Were interrupts not disabled by <b>ZTimerOn</b>, keyboard, mouse, timer, and other interrupts could occur during the timing interval, and the time required to service those interrupts would incorrectly and erratically appear to be part of the execution time of the code being measured. As a result, code timed with the Zen timer should not expect any hardware interrupts to occur during the interval between any call to <b>ZTimerOn</b> and the corresponding call to <b>ZTimerOff</b>, and should not enable interrupts during that time.</p>
|
||||
|
||||
<h3><a id="Heading7"></a>Time and the PC</h3>
|
||||
<h3 id="Heading7">Time and the PC</h3>
|
||||
|
||||
<p>A second interesting point about <b>ZTimerOn</b> is that it may introduce some small inaccuracy into the system clock time whenever it is called. To understand why this is so, we need to examine the way in which both the 8253 and the PC’s system clock (which keeps the current time) work.</p>
|
||||
|
||||
|
|
@ -59,9 +52,8 @@
|
|||
|
||||
<p>Timer 1 is dedicated to providing dynamic RAM refresh, and should not be tampered with lest system crashes result.</p>
|
||||
|
||||
<p><a id="Fig1"><!-- </A><A HREF="javascript:displayWindow('images/03-01.jpg',410,243 )"> --><img src="images/03-01.jpg" /><br />
|
||||
<!-- </A>
|
||||
<BR><A HREF="javascript:displayWindow('images/03-01.jpg',410,243)"> --><b>Figure 3.1</b></a> <i>The configuration of the 8253 timer chip in the PC.</i></p>
|
||||
<p><a id="Fig1"><img src="images/03-01.jpg" /><br />
|
||||
<b>Figure 3.1</b></a> <i>The configuration of the 8253 timer chip in the PC.</i></p>
|
||||
|
||||
<p>Finally, timer 0 is used to drive the system clock. As programmed by the BIOS at power-up, every 65,536 (64K) counts, or 54.925 milliseconds, timer 0 generates a rising edge on its output line. (A millisecond is one-thousandth of a second, and is abbreviated ms.) This line is connected to the hardware interrupt 0 (IRQ0) line on the system board, so every 54.925 ms, timer 0 causes hardware interrupt 0 to occur.</p>
|
||||
|
||||
|
|
@ -92,10 +84,6 @@
|
|||
|
||||
<div align="center">
|
||||
Graphics Programming Black Book © 2001 Michael Abrash
|
||||
</div><!-- all of the reference materials (books) have the footer and subfoot reveresed -->
|
||||
<!-- reference_subfoot = footer -->
|
||||
<!-- reference_footer = subfoot -->
|
||||
<!-- BEGIN SUB FOOTER -->
|
||||
<!-- END FOOTER -->
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
19
03-04.html
19
03-04.html
|
|
@ -10,16 +10,9 @@
|
|||
<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: Assume Nothing</title><!-- HEADER -->
|
||||
<!-- Empty Reference Subhead -->
|
||||
<!--ISBN=1576101746//-->
|
||||
<!--TITLE=Michael Abrash's Graphics Programming Black Book Special Edition//-->
|
||||
<!--AUTHOR=Michael Abrash//-->
|
||||
<!--PUBLISHER=The Coriolis Group, Inc.//-->
|
||||
<title>Michael Abrash's Graphics Programming Black Book Special Edition: Assume Nothing</title>
|
||||
<meta name="chapter" content="03" />
|
||||
<meta name="pages" content="045-048" />
|
||||
<!--UNASSIGNED1//-->
|
||||
<!--UNASSIGNED2//-->
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
|
@ -49,7 +42,7 @@
|
|||
|
||||
<p>Nonetheless, it’s a good idea to reboot your computer at the end of each session with the Zen timer in order to make sure that the system clock is correct.</p>
|
||||
|
||||
<h3><a id="Heading8"></a>Stopping the Zen Timer</h3>
|
||||
<h3 id="Heading8">Stopping the Zen Timer</h3>
|
||||
|
||||
<p>At some point after <b>ZTimerOn</b> is called, <b>ZTimerOff</b> must always be called to mark the end of the timing interval. <b>ZTimerOff</b> saves the context of the calling program, latches and reads the timer 0 count, converts that count from the countdown value that the timer maintains to the number of counts elapsed since <b>ZTimerOn</b> was called, and stores the result. Immediately after latching the timer 0 count—and before enabling interrupts—<b>ZTimerOff</b> checks the 8259 interrupt controller to see if there is a pending timer interrupt, setting a flag to mark that the timer overflowed if there is indeed a pending timer interrupt.</p>
|
||||
|
||||
|
|
@ -59,7 +52,7 @@
|
|||
|
||||
<p>One interesting aspect of <b>ZTimerOff</b> is the manner in which timer 0 is stopped in order to read the timer count. We don’t actually have to stop timer 0 to read the count; the 8253 provides a special latched read feature for the specific purpose of reading the count while a time is running. (That’s a good thing, too; we’ve no documented way to stop timer 0 if we wanted to, since its gate input isn’t connected. Later in this chapter, though, we’ll see that timer 0 can be stopped after all.) We simply tell the 8253 to latch the current count, and the 8253 does so without breaking stride.</p>
|
||||
|
||||
<h3><a id="Heading9"></a>Reporting Timing Results</h3>
|
||||
<h3 id="Heading9">Reporting Timing Results</h3>
|
||||
|
||||
<p><b>ZTimerReport</b> may be called to display timing results at any time after both <b>ZTimerOn</b> and <b>ZTimerOff</b> have been called. <b>ZTimerReport</b> first checks to see whether the timer overflowed (counted down to 0 and turned over) before <b>ZTimerOff</b> was called; if overflow did occur, <b>ZTimerOff</b> prints a message to that effect and returns. Otherwise, <b>ZTimerReport</b> subtracts the reference count (representing the overhead of the Zen timer) from the count measured between the calls to <b>ZTimerOn</b> and <b>ZTimerOff</b>, converts the result from timer counts to microseconds, and prints the resulting time in microseconds to the standard output.</p>
|
||||
|
||||
|
|
@ -94,10 +87,6 @@
|
|||
|
||||
<div align="center">
|
||||
Graphics Programming Black Book © 2001 Michael Abrash
|
||||
</div><!-- all of the reference materials (books) have the footer and subfoot reveresed -->
|
||||
<!-- reference_subfoot = footer -->
|
||||
<!-- reference_footer = subfoot -->
|
||||
<!-- BEGIN SUB FOOTER -->
|
||||
<!-- END FOOTER -->
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
27
03-05.html
27
03-05.html
|
|
@ -10,16 +10,9 @@
|
|||
<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: Assume Nothing</title><!-- HEADER -->
|
||||
<!-- Empty Reference Subhead -->
|
||||
<!--ISBN=1576101746//-->
|
||||
<!--TITLE=Michael Abrash's Graphics Programming Black Book Special Edition//-->
|
||||
<!--AUTHOR=Michael Abrash//-->
|
||||
<!--PUBLISHER=The Coriolis Group, Inc.//-->
|
||||
<title>Michael Abrash's Graphics Programming Black Book Special Edition: Assume Nothing</title>
|
||||
<meta name="chapter" content="03" />
|
||||
<meta name="pages" content="048-050" />
|
||||
<!--UNASSIGNED1//-->
|
||||
<!--UNASSIGNED2//-->
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
|
@ -37,7 +30,7 @@
|
|||
|
||||
<p><br /></p>
|
||||
|
||||
<h3><a id="Heading10"></a>Notes on the Zen Timer</h3>
|
||||
<h3 id="Heading10">Notes on the Zen Timer</h3>
|
||||
|
||||
<p>The Zen timer subroutines are designed to be near-called from assembly language code running in the public segment <b>Code</b>. The Zen timer subroutines can, however, be called from any assembly or high-level language code that generates OBJ files that are compatible with the Microsoft linker, simply by modifying the segment that the timer code runs in to match the segment used by the code being timed, or by changing the Zen timer routines to far procedures and making far calls to the Zen timer code from the code being timed, as discussed at the end of this chapter. All three subroutines preserve all registers and all flags except the interrupt flag, so calls to these routines are transparent to the calling code.</p>
|
||||
|
||||
|
|
@ -51,11 +44,11 @@
|
|||
|
||||
<p>Not that this variation between models makes the Zen timer one whit less useful—quite the contrary. The Zen timer is an excellent tool for evaluating code performance over the entire spectrum of PC-compatible computers.</p>
|
||||
|
||||
<h3><a id="Heading11"></a>A Sample Use of the Zen Timer</h3>
|
||||
<h3 id="Heading11">A Sample Use of the Zen Timer</h3>
|
||||
|
||||
<p>Listing 3.2 shows a test-bed program for measuring code performance with the Zen timer. This program sets DS equal to CS (for reasons we’ll discuss shortly), includes the code to be measured from the file TESTCODE, and calls <b>ZTimerReport</b> to display the timing results. Consequently, the code being measured should be in the file TESTCODE, and should contain calls to <b>ZTimerOn</b> and <b>ZTimerOff</b> .</p>
|
||||
|
||||
<p><b>LISTING 3.2 PZTEST.ASM</b></p><!-- CODE //-->
|
||||
<p><b>LISTING 3.2 PZTEST.ASM</b></p>
|
||||
<pre>
|
||||
; Program to measure performance of code that takes less than
|
||||
; 54 ms to execute. (PZTEST.ASM)
|
||||
|
|
@ -94,11 +87,11 @@ Start proc near
|
|||
Start endp
|
||||
Code ends
|
||||
end Start
|
||||
</pre><!-- END CODE //-->
|
||||
</pre>
|
||||
|
||||
<p>Listing 3.3 shows some sample code to be timed. This listing measures the time required to execute 1,000 loads of AL from the memory variable <b>MemVar</b> . Note that Listing 3.3 calls <b>ZTimerOn</b> to start timing, performs 1,000 <b>MOV</b> instructions in a row, and calls <b>ZTimerOff</b> to end timing. When Listing 3.2 is named TESTCODE and included by Listing 3.3, Listing 3.2 calls <b>ZTimerReport</b> to display the execution time after the code in Listing 3.3 has been run.</p>
|
||||
|
||||
<p><b>LISTING 3.3 LST3-3.ASM</b></p><!-- CODE //-->
|
||||
<p><b>LISTING 3.3 LST3-3.ASM</b></p>
|
||||
<pre>
|
||||
; Test file;
|
||||
; Measures the performance of 1,000 loads of AL from
|
||||
|
|
@ -124,7 +117,7 @@ Skip:
|
|||
; Stop timing.
|
||||
;
|
||||
call ZTimerOff
|
||||
</pre><!-- END CODE //-->
|
||||
</pre>
|
||||
|
||||
<p>It’s worth noting that Listing 3.3 begins by jumping around the memory variable <b>MemVar</b>. This approach lets us avoid reproducing Listing 3.2 in its entirety for each code fragment we want to measure; by defining any needed data right in the code segment and jumping around that data, each listing becomes self-contained and can be plugged directly into Listing 3.2 as TESTCODE. Listing 3.2 sets DS equal to CS before doing anything else precisely so that data can be embedded in code fragments being timed. Note that only after the initial jump is performed in Listing 3.3 is the Zen timer started, since we don’t want to include the execution time of start-up code in the timing interval. That’s why the calls to <b>ZTimerOn</b> and <b>ZTimerOff</b> are in TESTCODE, not in PZTEST.ASM; this way, we have full control over which portion of TESTCODE is timed, and we can keep set-up code and the like out of the timing interval.</p>
|
||||
|
||||
|
|
@ -145,10 +138,6 @@ Skip:
|
|||
|
||||
<div align="center">
|
||||
Graphics Programming Black Book © 2001 Michael Abrash
|
||||
</div><!-- all of the reference materials (books) have the footer and subfoot reveresed -->
|
||||
<!-- reference_subfoot = footer -->
|
||||
<!-- reference_footer = subfoot -->
|
||||
<!-- BEGIN SUB FOOTER -->
|
||||
<!-- END FOOTER -->
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
29
03-06.html
29
03-06.html
|
|
@ -10,16 +10,9 @@
|
|||
<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: Assume Nothing</title><!-- HEADER -->
|
||||
<!-- Empty Reference Subhead -->
|
||||
<!--ISBN=1576101746//-->
|
||||
<!--TITLE=Michael Abrash's Graphics Programming Black Book Special Edition//-->
|
||||
<!--AUTHOR=Michael Abrash//-->
|
||||
<!--PUBLISHER=The Coriolis Group, Inc.//-->
|
||||
<title>Michael Abrash's Graphics Programming Black Book Special Edition: Assume Nothing</title>
|
||||
<meta name="chapter" content="03" />
|
||||
<meta name="pages" content="051-053" />
|
||||
<!--UNASSIGNED1//-->
|
||||
<!--UNASSIGNED2//-->
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
|
@ -39,7 +32,7 @@
|
|||
|
||||
<p>Listing 3.3 is used by naming it TESTCODE, assembling both Listing 3.2 (which includes TESTCODE) and Listing 3.1 with TASM or MASM, and linking the two resulting OBJ files together by way of the Borland orMicrosoft linker. Listing 3.4 shows a batch file, PZTIME.BAT, which does all that; when run, this batch file generates and runs the executable file PZTEST.EXE. PZTIME.BAT (Listing 3.4) assumes that the file PZTIMER.ASM contains Listing 3.1, and the file PZTEST.ASM contains Listing 3.2. The command-line parameter to PZTIME.BAT is the name of the file to be copied to TESTCODE and included into PZTEST.ASM. (Note that Turbo Assembler can be substituted for MASM by replacing “masm” with “tasm” and “link” with “tlink” in Listing 3.4. The same is true of Listing 3.7.)</p>
|
||||
|
||||
<p><b>LISTING 3.4 PZTIME.BAT</b></p><!-- CODE //-->
|
||||
<p><b>LISTING 3.4 PZTIME.BAT</b></p>
|
||||
<pre>
|
||||
echo off
|
||||
rem
|
||||
|
|
@ -102,25 +95,25 @@ echo ***************************************************************
|
|||
echo * An error occurred while building the precision Zen timer. *
|
||||
echo ***************************************************************
|
||||
:end
|
||||
</pre><!-- END CODE //-->
|
||||
</pre>
|
||||
|
||||
<p>Assuming that Listing 3.3 is named LST3-3.ASM and Listing 3.4 is named PZTIME.BAT, the code in Listing 3.3 would be timed with the command:</p><!-- CODE SNIP //-->
|
||||
<p>Assuming that Listing 3.3 is named LST3-3.ASM and Listing 3.4 is named PZTIME.BAT, the code in Listing 3.3 would be timed with the command:</p>
|
||||
<pre>
|
||||
pztime LST3-3.ASM
|
||||
</pre><!-- END CODE SNIP //-->
|
||||
</pre>
|
||||
|
||||
<p>which performs all assembly and linking, and reports the execution time of the code in Listing 3.3.</p>
|
||||
|
||||
<p>When the above command is executed on an original 4.77 MHz IBM PC, the time reported by the Zen timer is 3619 µs, or about 3.62 µs per load of AL from memory. (While the exact number is 3.619 µs per load of AL, I’m going to round off that last digit from now on. No matter how many repetitions of a given instruction are timed, there’s just too much noise in the timing process—between dynamic RAM refresh, the prefetch queue, and the internal state of the processor at the start of timing—for that last digit to have any significance.) Given the test PC’s 4.77 MHz clock, this works out to about 17 cycles per <b>MOV</b>, which is actually a good bit longer than Intel’s specified 10-cycle execution time for this instruction. (See the MASM or TASM documentation, or Intel’s processor reference manuals, for official execution times.) Fear not, the Zen timer is right—<b>MOV AL,[MEMVAR]</b> really does take 17 cycles as used in Listing 3.3. Exactly why that is so is just what this book is all about.</p>
|
||||
|
||||
<p>In order to perform any of the timing tests in this book, enter Listing 3.1 and name it PZTIMER.ASM, enter Listing 3.2 and name it PZTEST.ASM, and enter Listing 3.4 and name it PZTIME.BAT. Then simply enter the listing you wish to run into the file <i>filename</i> and enter the command:</p><!-- CODE SNIP //-->
|
||||
<p>In order to perform any of the timing tests in this book, enter Listing 3.1 and name it PZTIMER.ASM, enter Listing 3.2 and name it PZTEST.ASM, and enter Listing 3.4 and name it PZTIME.BAT. Then simply enter the listing you wish to run into the file <i>filename</i> and enter the command:</p>
|
||||
<pre>
|
||||
pztime <filename>
|
||||
</pre><!-- END CODE SNIP //-->
|
||||
</pre>
|
||||
|
||||
<p>In fact, that’s exactly how I timed each of the listings in this book. Code fragments you write yourself can be timed in just the same way. If you wish to time code directly in place in your programs, rather than in the test-bed program of Listing 3.2, simply insert calls to <b>ZTimerOn, ZTimerOff</b>, and <b>ZTimerReport</b> in the appropriate places and link PZTIMER to your program.</p>
|
||||
|
||||
<h3><a id="Heading12"></a>The Long-Period Zen Timer</h3>
|
||||
<h3 id="Heading12">The Long-Period Zen Timer</h3>
|
||||
|
||||
<p>With a few exceptions, the Zen timer presented above will serve us well for the remainder of this book since we’ll be focusing on relatively short code sequences that generally take much less than 54 ms to execute. Occasionally, however, we will need to time longer intervals. What’s more, it is very likely that you will want to time code sequences longer than 54 ms at some point in your programming career. Accordingly, I’ve also developed a Zen timer for periods longer than 54 ms. The long-period Zen timer (so named by contrast with the precision Zen timer just presented) shown in Listing 3.5 can measure periods up to one hour in length.</p>
|
||||
|
||||
|
|
@ -147,10 +140,6 @@ pztime <filename>
|
|||
|
||||
<div align="center">
|
||||
Graphics Programming Black Book © 2001 Michael Abrash
|
||||
</div><!-- all of the reference materials (books) have the footer and subfoot reveresed -->
|
||||
<!-- reference_subfoot = footer -->
|
||||
<!-- reference_footer = subfoot -->
|
||||
<!-- BEGIN SUB FOOTER -->
|
||||
<!-- END FOOTER -->
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
21
03-07.html
21
03-07.html
|
|
@ -10,16 +10,9 @@
|
|||
<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: Assume Nothing</title><!-- HEADER -->
|
||||
<!-- Empty Reference Subhead -->
|
||||
<!--ISBN=1576101746//-->
|
||||
<!--TITLE=Michael Abrash's Graphics Programming Black Book Special Edition//-->
|
||||
<!--AUTHOR=Michael Abrash//-->
|
||||
<!--PUBLISHER=The Coriolis Group, Inc.//-->
|
||||
<title>Michael Abrash's Graphics Programming Black Book Special Edition: Assume Nothing</title>
|
||||
<meta name="chapter" content="03" />
|
||||
<meta name="pages" content="053-065" />
|
||||
<!--UNASSIGNED1//-->
|
||||
<!--UNASSIGNED2//-->
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
|
@ -41,7 +34,7 @@
|
|||
|
||||
<p>The long-period Zen timer has some of the same effects on the system time as does the precision Zen timer, so it’s a good idea to reboot the system after a session with the long-period Zen timer. The long-period Zen timer does not, however, have the same potential for introducing major inaccuracy into the system clock time during a single timing run since it leaves interrupts enabled and therefore allows the system clock to update normally.</p>
|
||||
|
||||
<h4 align="left"><a id="Heading13"></a>Stopping the Clock</h4>
|
||||
<h4 align="left" id="Heading13">Stopping the Clock</h4>
|
||||
|
||||
<p>There’s a potential problem with the long-period Zen timer. The problem is this: In order to measure times longer than 54 ms, we must maintain not one but two timing components, the timer 0 count and the BIOS time-of-day count. The time-of-day count measures the passage of 54.9 ms intervals, while the timer 0 count measures time within those 54.9 ms intervals. We need to read the two time components simultaneously in order to get a clean reading. Otherwise, we may read the timer count just before it turns over and generates an interrupt, then read the BIOS time-of-day count just after the interrupt has occurred and caused the time-of-day count to turn over, with a resulting 54 ms measurement inaccuracy. (The opposite sequence—reading the time-of-day count and then the timer count—can result in a 54 ms inaccuracy in the other direction.)</p>
|
||||
|
||||
|
|
@ -53,7 +46,7 @@
|
|||
|
||||
<p>I’ve set up Listing 3.5 so that it can assemble to either use or not use the undocumented timer-stopping feature, as you please. The <b>PS2</b> equate selects between the two modes of operation. If <b>PS2</b> is 1 (as it is in Listing 3.5), then the latch-and-read method is used; if <b>PS2</b> is 0, then the undocumented timer-stop approach is used. The latch-and-read method will work on all PC-compatible computers, but may occasionally produce results that are incorrect by 54 ms. The timer-stop approach avoids synchronization problems, but doesn’t work on all computers.</p>
|
||||
|
||||
<p><b>LISTING 3.5 LZTIMER.ASM</b></p><!-- CODE //-->
|
||||
<p><b>LISTING 3.5 LZTIMER.ASM</b></p>
|
||||
<pre>
|
||||
;
|
||||
; The long-period Zen timer. (LZTIMER.ASM)
|
||||
|
|
@ -689,7 +682,7 @@ ZTimerReport endp
|
|||
|
||||
Code ends
|
||||
end
|
||||
</pre><!-- END CODE //-->
|
||||
</pre>
|
||||
|
||||
<p><br /></p>
|
||||
|
||||
|
|
@ -708,10 +701,6 @@ Code ends
|
|||
|
||||
<div align="center">
|
||||
Graphics Programming Black Book © 2001 Michael Abrash
|
||||
</div><!-- all of the reference materials (books) have the footer and subfoot reveresed -->
|
||||
<!-- reference_subfoot = footer -->
|
||||
<!-- reference_footer = subfoot -->
|
||||
<!-- BEGIN SUB FOOTER -->
|
||||
<!-- END FOOTER -->
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
21
03-08.html
21
03-08.html
|
|
@ -10,16 +10,9 @@
|
|||
<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: Assume Nothing</title><!-- HEADER -->
|
||||
<!-- Empty Reference Subhead -->
|
||||
<!--ISBN=1576101746//-->
|
||||
<!--TITLE=Michael Abrash's Graphics Programming Black Book Special Edition//-->
|
||||
<!--AUTHOR=Michael Abrash//-->
|
||||
<!--PUBLISHER=The Coriolis Group, Inc.//-->
|
||||
<title>Michael Abrash's Graphics Programming Black Book Special Edition: Assume Nothing</title>
|
||||
<meta name="chapter" content="03" />
|
||||
<meta name="pages" content="065-067" />
|
||||
<!--UNASSIGNED1//-->
|
||||
<!--UNASSIGNED2//-->
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
|
@ -47,13 +40,13 @@
|
|||
|
||||
<p>Finally, please note that the <i>precision</i> Zen timer works perfectly well on both PS/2 and non-PS/2 computers. The PS/2 and 8253 considerations we’ve just discussed apply <i>only</i> to the longZen timer.</p>
|
||||
|
||||
<h3><a id="Heading14"></a>Example Use of the Long-Period Zen Timer</h3>
|
||||
<h3 id="Heading14">Example Use of the Long-Period Zen Timer</h3>
|
||||
|
||||
<p>The long-period Zen timer has exactly the same calling interface as the precision Zen timer, and can be used in place of the precision Zen timer simply by linking it to the code to be timed in place of linking the precision timer code. Whenever the precision Zen timer informs you that the code being timed takes too long for the precision timer to handle, all you have to do is link in the long-period timer instead.</p>
|
||||
|
||||
<p>Listing 3.6 shows a test-bed program for the long-period Zen timer. While this program is similar to Listing 3.2, it’s worth noting that Listing 3.6 waits for a few seconds before calling <b>ZTimerOn</b>, thereby allowing any pending keyboard interrupts to be processed. Since interrupts must be left on in order to time periods longer than 54 ms, the interrupts generated by keystrokes (including the upstroke of the Enter key press that starts the program)—or any other interrupts, for that matter—could incorrectly inflate the time recorded by the long-period Zen timer. In light of this, resist the temptation to type ahead, move the mouse, or the like while the long-period Zen timer is timing.</p>
|
||||
|
||||
<p><b>LISTING 3.6 LZTEST.ASM</b></p><!-- CODE //-->
|
||||
<p><b>LISTING 3.6 LZTEST.ASM</b></p>
|
||||
<pre>
|
||||
; Program to measure performance of code that takes longer than
|
||||
; 54 ms to execute. (LZTEST.ASM)
|
||||
|
|
@ -113,7 +106,7 @@ CheckDelayTime:
|
|||
Start endp
|
||||
Code ends
|
||||
end Start
|
||||
</pre><!-- END CODE //-->
|
||||
</pre>
|
||||
|
||||
<p>As with the precision Zen timer, the program in Listing 3.6 is used by naming the file containing the code to be timed TESTCODE, then assembling both Listing 3.6 and Listing 3.5 with MASM or TASM and linking the two files together by way of the Microsoft or Borland linker. Listing 3.7 shows a batch file, named LZTIME.BAT, which does all of the above, generating and running the executable file LZTEST.EXE. LZTIME.BAT assumes that the file LZTIMER.ASM contains Listing 3.5 and the file LZTEST.ASM contains Listing 3.6.</p>
|
||||
|
||||
|
|
@ -134,10 +127,6 @@ Code ends
|
|||
|
||||
<div align="center">
|
||||
Graphics Programming Black Book © 2001 Michael Abrash
|
||||
</div><!-- all of the reference materials (books) have the footer and subfoot reveresed -->
|
||||
<!-- reference_subfoot = footer -->
|
||||
<!-- reference_footer = subfoot -->
|
||||
<!-- BEGIN SUB FOOTER -->
|
||||
<!-- END FOOTER -->
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
33
03-09.html
33
03-09.html
|
|
@ -10,16 +10,9 @@
|
|||
<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: Assume Nothing</title><!-- HEADER -->
|
||||
<!-- Empty Reference Subhead -->
|
||||
<!--ISBN=1576101746//-->
|
||||
<!--TITLE=Michael Abrash's Graphics Programming Black Book Special Edition//-->
|
||||
<!--AUTHOR=Michael Abrash//-->
|
||||
<!--PUBLISHER=The Coriolis Group, Inc.//-->
|
||||
<title>Michael Abrash's Graphics Programming Black Book Special Edition: Assume Nothing</title>
|
||||
<meta name="chapter" content="03" />
|
||||
<meta name="pages" content="067-070" />
|
||||
<!--UNASSIGNED1//-->
|
||||
<!--UNASSIGNED2//-->
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
|
@ -37,7 +30,7 @@
|
|||
|
||||
<p><br /></p>
|
||||
|
||||
<p><b>LISTING 3.7 LZTIME.BAT</b></p><!-- CODE //-->
|
||||
<p><b>LISTING 3.7 LZTIME.BAT</b></p>
|
||||
<pre>
|
||||
echo off
|
||||
rem
|
||||
|
|
@ -100,11 +93,11 @@ echo ***************************************************************
|
|||
echo * An error occurred while building the long-period Zen timer. *
|
||||
echo ***************************************************************
|
||||
:end
|
||||
</pre><!-- END CODE //-->
|
||||
</pre>
|
||||
|
||||
<p>Listing 3.8 shows sample code that can be timed with the test-bed program of Listing 3.6. Listing 3.8 measures the time required to execute 20,000 loads of AL from memory, a length of time too long for the precision Zen timer to handle on the 8088.</p>
|
||||
|
||||
<p><b>LISTING 3.8 LST3-8.ASM</b></p><!-- CODE //-->
|
||||
<p><b>LISTING 3.8 LST3-8.ASM</b></p>
|
||||
<pre>
|
||||
;
|
||||
; Measures the performance of 20,000 loads of AL from
|
||||
|
|
@ -133,25 +126,25 @@ endm
|
|||
; Stop timing.
|
||||
;
|
||||
callZTimerOff
|
||||
</pre><!-- END CODE //-->
|
||||
</pre>
|
||||
|
||||
<p>When LZTIME.BAT is run on a PC with the following command line (assuming the code in Listing 3.8 is the file LST3-8.ASM)</p><!-- CODE SNIP //-->
|
||||
<p>When LZTIME.BAT is run on a PC with the following command line (assuming the code in Listing 3.8 is the file LST3-8.ASM)</p>
|
||||
<pre>
|
||||
lztime lst3-8.asm
|
||||
</pre><!-- END CODE SNIP //-->
|
||||
</pre>
|
||||
|
||||
<p>the result is 72,544 µs, or about 3.63 µs per load of AL from memory. This is just slightly longer than the time per load of AL measured by the precision Zen timer, as we would expect given that interrupts are left enabled by the long-period Zen timer. The extra fraction of a microsecond measured per <b>MOV</b> reflects the time required to execute the BIOS code that handles the 18.2 timer interrupts that occur each second.</p>
|
||||
|
||||
<p>Note that the command can take as much as 10 minutes to finish on a slow PC if you are using MASM, with most of that time spent assembling Listing 3.8. Why? Because MASM is notoriously slow at assembling <b>REPT</b> blocks, and the block in Listing 3.8 is repeated 20,000 times.</p>
|
||||
|
||||
<h3><a id="Heading15"></a>Using the Zen Timer from C</h3>
|
||||
<h3 id="Heading15">Using the Zen Timer from C</h3>
|
||||
|
||||
<p>The Zen timer can be used to measure code performance when programming in C—but not right out of the box. As presented earlier, the timer is designed to be called from assembly language; some relatively minor modifications are required before the <b>ZTimerOn</b> (start timer), <b>ZTimerOff</b> (stop timer), and <b>ZTimerReport</b> (display timing results) routines can be called from C. There are two separate cases to be dealt with here: small code model and large; I’ll tackle the simpler one, the small code model, first.</p>
|
||||
|
||||
<p>Altering the Zen timer for linking to a small code model C program involves the following steps: <b>C</b> hange <b>ZTimerOn</b> to <b>_ZTimerOn</b>, change <b>ZTimerOff</b> to <b>_ZTimerOff</b>, change <b>ZTimerReport</b> to <b>_ZTimerReport</b>, and change <b>Code</b> to <b>_TEXT</b> . Figure 3.2 shows the line numbers and new states of all lines from Listing 3.1 that must be changed. These changes convert the code to use C-style external label names and the small model C code segment. (In C<small>++</small>, use the “C” specifier, as in</p><!-- CODE SNIP //-->
|
||||
<p>Altering the Zen timer for linking to a small code model C program involves the following steps: <b>C</b> hange <b>ZTimerOn</b> to <b>_ZTimerOn</b>, change <b>ZTimerOff</b> to <b>_ZTimerOff</b>, change <b>ZTimerReport</b> to <b>_ZTimerReport</b>, and change <b>Code</b> to <b>_TEXT</b> . Figure 3.2 shows the line numbers and new states of all lines from Listing 3.1 that must be changed. These changes convert the code to use C-style external label names and the small model C code segment. (In C<small>++</small>, use the “C” specifier, as in</p>
|
||||
<pre>
|
||||
extern “C” ZTimerOn(void);
|
||||
</pre><!-- END CODE SNIP //-->
|
||||
</pre>
|
||||
|
||||
<p><br /></p>
|
||||
|
||||
|
|
@ -170,10 +163,6 @@ extern “C” ZTimerOn(void);
|
|||
|
||||
<div align="center">
|
||||
Graphics Programming Black Book © 2001 Michael Abrash
|
||||
</div><!-- all of the reference materials (books) have the footer and subfoot reveresed -->
|
||||
<!-- reference_subfoot = footer -->
|
||||
<!-- reference_footer = subfoot -->
|
||||
<!-- BEGIN SUB FOOTER -->
|
||||
<!-- END FOOTER -->
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
43
03-10.html
43
03-10.html
|
|
@ -10,16 +10,9 @@
|
|||
<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: Assume Nothing</title><!-- HEADER -->
|
||||
<!-- Empty Reference Subhead -->
|
||||
<!--ISBN=1576101746//-->
|
||||
<!--TITLE=Michael Abrash's Graphics Programming Black Book Special Edition//-->
|
||||
<!--AUTHOR=Michael Abrash//-->
|
||||
<!--PUBLISHER=The Coriolis Group, Inc.//-->
|
||||
<title>Michael Abrash's Graphics Programming Black Book Special Edition: Assume Nothing</title>
|
||||
<meta name="chapter" content="03" />
|
||||
<meta name="pages" content="070-073" />
|
||||
<!--UNASSIGNED1//-->
|
||||
<!--UNASSIGNED2//-->
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
|
@ -39,43 +32,41 @@
|
|||
|
||||
<p>when declaring the timer routines <b>extern</b>, so that name-mangling doesn’t occur, and the linker can find the routines’ C-style names.)</p>
|
||||
|
||||
<p>That’s all it takes; after doing this, you’ll be able to use the Zen timer from C, as, for example, in:</p><!-- CODE SNIP //-->
|
||||
<p>That’s all it takes; after doing this, you’ll be able to use the Zen timer from C, as, for example, in:</p>
|
||||
<pre>
|
||||
ZTimerOn():
|
||||
for (i=0, x=0; i<100; i++)
|
||||
x += i;
|
||||
ZTimerOff();
|
||||
ZTimerReport();
|
||||
</pre><!-- END CODE SNIP //-->
|
||||
</pre>
|
||||
|
||||
<p>(I’m talking about the precision timer here. The long-period timer—Listing 3.5—requires the same modifications, but to different lines.)</p>
|
||||
|
||||
<p><a id="Fig2"><!-- </A><A HREF="javascript:displayWindow('images/03-02.jpg',410,189 )"> --><img src="images/03-02.jpg" /><br />
|
||||
<!-- </A>
|
||||
<BR><A HREF="javascript:displayWindow('images/03-02.jpg',410,189)"> --><b>Figure 3.2</b></a> <i>Changes for use with small code model C.</i></p>
|
||||
<p><a id="Fig2"><img src="images/03-02.jpg" /><br />
|
||||
<b>Figure 3.2</b></a> <i>Changes for use with small code model C.</i></p>
|
||||
|
||||
<p>Altering the Zen timer for use in C’s large code model is a tad more complex, because in addition to the above changes, all functions, including the internal reference timing routines that are used to calculate overhead so it can be subtracted out, must be converted to far. Figure 3.3 shows the line numbers and new states of all lines from Listing 3.1 that must be changed in order to call the Zen timer from large code model C. Again, the line numbers are specific to the precision timer, but the long-period timer is very similar.</p>
|
||||
|
||||
<p>The full listings for the C-callable Zen timers are presented in Chapter K on the companion CD-ROM.</p>
|
||||
|
||||
<h4 align="left"><a id="Heading16"></a>Watch Out for Optimizing Assemblers!</h4>
|
||||
<h4 align="left" id="Heading16">Watch Out for Optimizing Assemblers!</h4>
|
||||
|
||||
<p>One important safety tip when modifying the Zen timer for use with large code model C code: Watch out for optimizing assemblers! TASM actually replaces</p><!-- CODE SNIP //-->
|
||||
<p>One important safety tip when modifying the Zen timer for use with large code model C code: Watch out for optimizing assemblers! TASM actually replaces</p>
|
||||
<pre>
|
||||
call far ptr ReferenceZTimerOn
|
||||
</pre><!-- END CODE SNIP //-->
|
||||
</pre>
|
||||
|
||||
<p>with</p><!-- CODE SNIP //-->
|
||||
<p>with</p>
|
||||
<pre>
|
||||
push cs
|
||||
call near ptr ReferenceZTimerOn
|
||||
</pre><!-- END CODE SNIP //-->
|
||||
</pre>
|
||||
|
||||
<p>(and likewise for <b>ReferenceZTimerOff</b> ), which works because <b>ReferenceZTimerOn</b> is in the same segment as the calling code. This is normally a great optimization, being both smaller and faster than a far call. However, it’s not so great for the Zen</p>
|
||||
|
||||
<p><a id="Fig3"><!-- </A><A HREF="javascript:displayWindow('images/03-03.jpg',413,221 )"> --><img src="images/03-03.jpg" /><br />
|
||||
<!-- </A>
|
||||
<BR><A HREF="javascript:displayWindow('images/03-03.jpg',413,221)"> --><b>Figure 3.3</b></a> <i>Changes for use with large code model C.</i></p>
|
||||
<p><a id="Fig3"><img src="images/03-03.jpg" /><br />
|
||||
<b>Figure 3.3</b></a> <i>Changes for use with large code model C.</i></p>
|
||||
|
||||
<p>timer, because our purpose in calling the reference timing code is to determine exactly how much time is taken by overhead code—including the far calls to <b>ZTimerOn</b> and <b>ZTimerOf</b>f! By converting the far calls to push/near call pairs within the Zen timer module, TASM makes it impossible to emulate exactly the overhead of the Zen timer, and makes timings slightly (about 16 cycles on a 386) less accurate.</p>
|
||||
|
||||
|
|
@ -85,13 +76,13 @@ call near ptr ReferenceZTimerOn
|
|||
|
||||
<p>I’ve tested the changes shown in Figures 3.2 and 3.3 with TASM and Borland C<small>++</small> 4.0, and also with the latest MASM and Microsoft C/C<small>++</small> compiler.</p>
|
||||
|
||||
<h4 align="left"><a id="Heading17"></a>Further Reading</h4>
|
||||
<h4 align="left" id="Heading17">Further Reading</h4>
|
||||
|
||||
<p>For those of you who wish to pursue the mechanics of code measurement further, one good article about measuring code performance with the 8253 timer is “Programming Insight: High-Performance Software Analysis on the IBM PC,” by Byron Sheppard, which appeared in the January, 1987 issue of <i>Byte</i>. For complete if somewhat cryptic information on the 8253 timer itself, I refer you to Intel’s <i>Microsystem Components Handbook</i>, which is also a useful reference for a number of other PC components, including the 8259 Programmable Interrupt Controller and the 8237 DMA Controller. For details about the way the 8253 is used in the PC, as well as a great deal of additional information about the PC’s hardware and BIOS resources, I suggest you consult IBM’s series of technical reference manuals for the PC, XT, AT, Model 30, and microchannel computers, such as the Models 50, 60, and 80.</p>
|
||||
|
||||
<p>For our purposes, however, it’s not critical that you understand exactly how the Zen timer works. All you really need to know is what the Zen timer can do and how to use it, and we’ve accomplished that in this chapter.</p>
|
||||
|
||||
<h4 align="left"><a id="Heading18"></a>Armed with the Zen Timer, Onward and Upward</h4>
|
||||
<h4 align="left" id="Heading18">Armed with the Zen Timer, Onward and Upward</h4>
|
||||
|
||||
<p>The Zen timer is not perfect. For one thing, the finest resolution to which it can measure an interval is at best about 1µs, a period of time in which a 66 MHz Pentium computer can execute as many as 132 instructions (although an 8088-based PC would be hard-pressed to manage two instructions in a microsecond). Another problem is that the timing code itself interferes with the state of the prefetch queue and processor cache at the start of the code being timed, because the timing code is not necessarily fetched and does not necessarily access memory in exactly the same time sequence as the code immediately preceding the code under measurement normally does. This prefetch effect can introduce as much as 3 to 4 µ of inaccuracy. Similarly, the state of the prefetch queue at the end of the code being timed affects how long the code that stops the timer takes to execute. Consequently, the Zen timer tends to be more accurate for longer code sequences, since the relative magnitude of the inaccuracy introduced by the Zen timer becomes less over longer periods.</p>
|
||||
|
||||
|
|
@ -114,10 +105,6 @@ call near ptr ReferenceZTimerOn
|
|||
|
||||
<div align="center">
|
||||
Graphics Programming Black Book © 2001 Michael Abrash
|
||||
</div><!-- all of the reference materials (books) have the footer and subfoot reveresed -->
|
||||
<!-- reference_subfoot = footer -->
|
||||
<!-- reference_footer = subfoot -->
|
||||
<!-- BEGIN SUB FOOTER -->
|
||||
<!-- END FOOTER -->
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
27
04-01.html
27
04-01.html
|
|
@ -10,16 +10,9 @@
|
|||
<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: In the Lair of the Cycle-Eaters</title><!-- HEADER -->
|
||||
<!-- Empty Reference Subhead -->
|
||||
<!--ISBN=1576101746//-->
|
||||
<!--TITLE=Michael Abrash's Graphics Programming Black Book Special Edition//-->
|
||||
<!--AUTHOR=Michael Abrash//-->
|
||||
<!--PUBLISHER=The Coriolis Group, Inc.//-->
|
||||
<title>Michael Abrash's Graphics Programming Black Book Special Edition: In the Lair of the Cycle-Eaters</title>
|
||||
<meta name="chapter" content="04" />
|
||||
<meta name="pages" content="075-080" />
|
||||
<!--UNASSIGNED1//-->
|
||||
<!--UNASSIGNED2//-->
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
|
@ -37,10 +30,10 @@
|
|||
|
||||
<p><br /></p>
|
||||
|
||||
<h2><a id="Heading1"></a>Chapter 4<br />
|
||||
<h2 id="Heading1">Chapter 4<br />
|
||||
In the Lair of the Cycle-Eaters</h2>
|
||||
|
||||
<h3><a id="Heading2"></a>How the PC Hardware Devours Code Performance</h3>
|
||||
<h3 id="Heading2">How the PC Hardware Devours Code Performance</h3>
|
||||
|
||||
<p>This chapter, adapted from my earlier book, <i>Zen of Assembly Language</i> located on the companion CD-ROM, goes right to the heart of my philosophy of optimization: Understand where the time really goes when your code runs. That may sound ridiculously simple, but, as this chapter makes clear, it turns out to be a challenging task indeed, one that at times verges on black magic. This chapter is a long-time favorite of mine because it was the first—and to a large extent only—work that I know of that discussed this material, thereby introducing a generation of PC programmers to pedal-to-the-metal optimization.</p>
|
||||
|
||||
|
|
@ -48,7 +41,7 @@
|
|||
|
||||
<p>So, don’t take either the absolute or the relative execution times presented in this chapter as gospel for newer processors, and read on to later chapters to see how the cycle-eaters and optimization rules have changed over time, but do take the time to at least skim through this chapter to give yourself a good start on the material in the rest of this book.</p>
|
||||
|
||||
<h3><a id="Heading3"></a>Cycle-Eaters</h3>
|
||||
<h3 id="Heading3">Cycle-Eaters</h3>
|
||||
|
||||
<p>Programming has many levels, ranging from the familiar (high-level languages, DOS calls, and the like) down to the esoteric things that lie on the shadowy edge of hardware-land. I call these <i>cycle-eaters</i> because, like the monsters in a bad 50s horror movie, they lurk in those shadows, taking their share of your program’s performance without regard to the forces of goodness or the U.S. Army. In this chapter, we’re going to jump right in at the lowest level by examining the cycle-eaters that live beneath the programming interface; that is, beneath your application, DOS, and BIOS—in fact, beneath the instruction set itself.</p>
|
||||
|
||||
|
|
@ -58,13 +51,13 @@
|
|||
|
||||
<p>Which brings us to cycle-eaters.</p>
|
||||
|
||||
<h3><a id="Heading4"></a>The Nature of Cycle-Eaters</h3>
|
||||
<h3 id="Heading4">The Nature of Cycle-Eaters</h3>
|
||||
|
||||
<p>Cycle-eaters are gremlins that live on the bus or in peripherals (and sometimes within the CPU itself), slowing the performance of PC code so that it doesn’t execute at full speed. Most cycle-eaters (and all of those haunting the older Intel processors) live outside the CPU’s Execution Unit, where they can <i>only</i> affect the CPU when the CPU performs a bus access (a memory or I/O read or write). Once your code and data are already inside the CPU, those cycle-eaters can no longer be a problem. Only on the 486 and Pentium CPUs will you find cycle-eaters inside the chip, as we’ll see in later chapters.</p>
|
||||
|
||||
<p>The nature and severity of the cycle-eaters vary enormously from processor to processor, and (especially) from memory architecture to memory architecture. In order to understand them all, we need first to understand the simplest among them, those that haunted the original 8088-based IBM PC. Later on in this book, I’ll be better able to explain the newer generation of cycle-eaters in terms of those ancestral cycle-eaters—but we have to get the groundwork down first.</p>
|
||||
|
||||
<h4 align="left"><a id="Heading5"></a>The 8088’s Ancestral Cycle-Eaters</h4>
|
||||
<h4 align="left" id="Heading5">The 8088’s Ancestral Cycle-Eaters</h4>
|
||||
|
||||
<p>Internally, the 8088 is a 16-bit processor, capable of running at full speed at all times—unless external data is required. External data must traverse the 8088’s external data bus and the PC’s data bus one byte at a time to and from peripherals, with cycle-eaters lurking along every step of the way. What’s more, external data includes not only memory operands <i>but also instruction bytes,</i> so even instructions with no memory operands can suffer from cycle-eaters. Since some of the 8088’s fastest instructions are register-only instructions, that’s important indeed.</p>
|
||||
|
||||
|
|
@ -82,7 +75,7 @@
|
|||
|
||||
<p>The locations of these cycle-eaters in the primordial 8088-based PC are shown in Figure 4.1. We’ll cover each of the cycle-eaters in turn in this chapter. The material won’t be easy since cycle-eaters are among the most subtle aspects of assembly programming. By the same token, however, this will be one of the most important and rewarding chapters in this book. Don’t worry if you don’t catch everything in this chapter, but do read it all even if the going gets a bit tough. Cycle-eaters play a key role in later chapters, so some familiarity with them is highly desirable.</p>
|
||||
|
||||
<h3><a id="Heading6"></a>The 8-Bit Bus Cycle-Eater</h3>
|
||||
<h3 id="Heading6">The 8-Bit Bus Cycle-Eater</h3>
|
||||
|
||||
<p><i>Look! Down on the motherboard! It’s a 16-bit processor! It’s an 8-bit processor! It’s...</i></p>
|
||||
|
||||
|
|
@ -109,10 +102,6 @@
|
|||
|
||||
<div align="center">
|
||||
Graphics Programming Black Book © 2001 Michael Abrash
|
||||
</div><!-- all of the reference materials (books) have the footer and subfoot reveresed -->
|
||||
<!-- reference_subfoot = footer -->
|
||||
<!-- reference_footer = subfoot -->
|
||||
<!-- BEGIN SUB FOOTER -->
|
||||
<!-- END FOOTER -->
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
53
04-02.html
53
04-02.html
|
|
@ -10,16 +10,9 @@
|
|||
<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: In the Lair of the Cycle-Eaters</title><!-- HEADER -->
|
||||
<!-- Empty Reference Subhead -->
|
||||
<!--ISBN=1576101746//-->
|
||||
<!--TITLE=Michael Abrash's Graphics Programming Black Book Special Edition//-->
|
||||
<!--AUTHOR=Michael Abrash//-->
|
||||
<!--PUBLISHER=The Coriolis Group, Inc.//-->
|
||||
<title>Michael Abrash's Graphics Programming Black Book Special Edition: In the Lair of the Cycle-Eaters</title>
|
||||
<meta name="chapter" content="04" />
|
||||
<meta name="pages" content="080-084" />
|
||||
<!--UNASSIGNED1//-->
|
||||
<!--UNASSIGNED2//-->
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
|
@ -37,13 +30,11 @@
|
|||
|
||||
<p><br /></p>
|
||||
|
||||
<p><a id="Fig1"><!-- </A><A HREF="javascript:displayWindow('images/04-01.jpg',411,379 )"> --><img src="images/04-01.jpg" /><br />
|
||||
<!-- </A>
|
||||
<BR><A HREF="javascript:displayWindow('images/04-01.jpg',411,379)"> --><b>Figure 4.1</b></a> <i>The location of the major cycle-eaters in the IBM PC.</i></p>
|
||||
<p><a id="Fig1"><img src="images/04-01.jpg" /><br />
|
||||
<b>Figure 4.1</b></a> <i>The location of the major cycle-eaters in the IBM PC.</i></p>
|
||||
|
||||
<p><a id="Fig2"><!-- </A><A HREF="javascript:displayWindow('images/04-02.jpg',418,236 )"> --><img src="images/04-02.jpg" /><br />
|
||||
<!-- </A>
|
||||
<BR><A HREF="javascript:displayWindow('images/04-02.jpg',418,236)"> --><b>Figure 4.2</b></a> <i>Internal data bus widths of the 8088.</i></p>
|
||||
<p><a id="Fig2"><img src="images/04-02.jpg" /><br />
|
||||
<b>Figure 4.2</b></a> <i>Internal data bus widths of the 8088.</i></p>
|
||||
|
||||
<p>As shown in Figure 4.1, the 8-bit bus cycle-eater lies squarely on the 8088’s external data bus. Technically, it might be more accurate to place this cycle-eater in the Bus Interface Unit, which breaks 16-bit memory accesses into paired 8-bit accesses, but it is really the limited width of the external data bus that constricts data flow into and out of the 8088. True, the original PC’s bus is also only 8 bits wide, but that’s just to match the 8088’s 8-bit bus; even if the PC’s bus were 16 bits wide, data could still pass into and out of the 8088 chip itself only 1 byte at a time.</p>
|
||||
|
||||
|
|
@ -51,39 +42,39 @@
|
|||
|
||||
<p>A related cycle-eater lurks beneath the 386SX chip, which is a 32-bit processor internally with only a 16-bit path to system memory. The numbers are different, but the way the cycle-eater operates is exactly the same. AT-compatible systems have 16-bit data buses, which can access a full 16-bit word at a time. The 386SX can process 32 bits (a doubleword) at a time, however, and loses a lot of time fetching that doubleword from memory in two halves.</p>
|
||||
|
||||
<h4 align="left"><a id="Heading7"></a>The Impact of the 8-Bit Bus Cycle-Eater</h4>
|
||||
<h4 align="left" id="Heading7">The Impact of the 8-Bit Bus Cycle-Eater</h4>
|
||||
|
||||
<p>One obvious effect of the 8-bit bus cycle-eater is that word-sized accesses to memory operands on the 8088 take 4 cycles longer than byte-sized accesses. That’s why the official instruction timings indicate that for code running on an 8088 an additional 4 cycles are required for every word-sized access to a memory operand. For instance,</p><!-- CODE SNIP //-->
|
||||
<p>One obvious effect of the 8-bit bus cycle-eater is that word-sized accesses to memory operands on the 8088 take 4 cycles longer than byte-sized accesses. That’s why the official instruction timings indicate that for code running on an 8088 an additional 4 cycles are required for every word-sized access to a memory operand. For instance,</p>
|
||||
<pre>
|
||||
mov ax,word ptr [MemVar]
|
||||
</pre><!-- END CODE SNIP //-->
|
||||
</pre>
|
||||
|
||||
<p>takes 4 cycles longer to read the word at address <b>MemVar</b> than</p><!-- CODE SNIP //-->
|
||||
<p>takes 4 cycles longer to read the word at address <b>MemVar</b> than</p>
|
||||
<pre>
|
||||
mov al,byte ptr [MemVar]
|
||||
</pre><!-- END CODE SNIP //-->
|
||||
</pre>
|
||||
|
||||
<p>takes to read the byte at address <b>MemVar.</b> (Actually, the difference between the two isn’t very likely to be exactly 4 cycles, for reasons that will become clear once we discuss the prefetch queue and dynamic RAM refresh cycle-eaters later in this chapter.)</p>
|
||||
|
||||
<p>What’s more, in some cases one instruction can perform multiple word-sized accesses, incurring that 4-cycle penalty on each access. For example, adding a value to a word-sized memory variable requires two word-sized accesses—one to read the destination operand from memory prior to adding to it, and one to write the result of the addition back to the destination operand—and thus incurs not one but two 4-cycle penalties. As a result</p><!-- CODE SNIP //-->
|
||||
<p>What’s more, in some cases one instruction can perform multiple word-sized accesses, incurring that 4-cycle penalty on each access. For example, adding a value to a word-sized memory variable requires two word-sized accesses—one to read the destination operand from memory prior to adding to it, and one to write the result of the addition back to the destination operand—and thus incurs not one but two 4-cycle penalties. As a result</p>
|
||||
<pre>
|
||||
add word ptr [MemVar],ax
|
||||
</pre><!-- END CODE SNIP //-->
|
||||
</pre>
|
||||
|
||||
<p>takes about 8 cycles longer to execute than:</p><!-- CODE SNIP //-->
|
||||
<p>takes about 8 cycles longer to execute than:</p>
|
||||
<pre>
|
||||
add byte ptr [MemVar],al
|
||||
</pre><!-- END CODE SNIP //-->
|
||||
</pre>
|
||||
|
||||
<p>String instructions can suffer from the 8-bit bus cycle-eater to a greater extent than other instructions. Believe it or not, a single <b>REP MOVSW</b> instruction can lose as much as 131,070 word-sized memory accesses x 4 cycles, or <i>524,280 cycles</i> to the 8-bit bus cycle-eater! In other words, one 8088 instruction (admittedly, an instruction that does a great deal) can take over one-tenth of a second longer on an 8088 than on an 8086, simply because of the 8-bit bus. <i>One-tenth of a second!</i> That’s a phenomenally long time in computer terms; in one-tenth of a second, the 8088 can perform more than 50,000 additions and subtractions.</p>
|
||||
|
||||
<p>The upshot of all this is simply that the 8088 can transfer word-sized data to and from memory at only half the speed of the 8086, which inevitably causes performance problems when coupled with an Execution Unit that can process word-sized data every bit as quickly as an 8086. These problems show up with any code that uses word-sized memory operands. More ominously, as we will see shortly, the 8-bit bus cycle-eater can cause performance problems with other sorts of code as well.</p>
|
||||
|
||||
<h4 align="left"><a id="Heading8"></a>What to Do about the 8-Bit Bus Cycle-Eater?</h4>
|
||||
<h4 align="left" id="Heading8">What to Do about the 8-Bit Bus Cycle-Eater?</h4>
|
||||
|
||||
<p>The obvious implication of the 8-bit bus cycle-eater is that byte-sized memory variables should be used whenever possible. After all, the 8088 performs <i>byte-sized</i> memory accesses just as quickly as the 8086. For instance, Listing 4.1, which uses a byte-sized memory variable as a loop counter, runs in 10.03 s per loop. That’s 20 percent faster than the 12.05 µs per loop execution time of Listing 4.2, which uses a word-sized counter. Why the difference in execution times? Simply because each word-sized <b>DEC</b> performs 4 byte-sized memory accesses (two to read the word-sized operand and two to write the result back to memory), while each byte-sized <b>DEC</b> performs only 2 byte-sized memory accesses in all.</p>
|
||||
|
||||
<p><b>LISTING 4.1 LST4-1.ASM</b></p><!-- CODE //-->
|
||||
<p><b>LISTING 4.1 LST4-1.ASM</b></p>
|
||||
<pre>
|
||||
; Measures the performance of a loop which uses a
|
||||
; byte-sized memory variable as the loop counter.
|
||||
|
|
@ -98,9 +89,9 @@ LoopTop:
|
|||
dec [Counter]
|
||||
jnz LoopTop
|
||||
call ZTimerOff
|
||||
</pre><!-- END CODE //-->
|
||||
</pre>
|
||||
|
||||
<p><b>LISTING 4.2 LST4-2.ASM</b></p><!-- CODE //-->
|
||||
<p><b>LISTING 4.2 LST4-2.ASM</b></p>
|
||||
<pre>
|
||||
; Measures the performance of a loop which uses a
|
||||
; word-sized memory variable as the loop counter.
|
||||
|
|
@ -115,7 +106,7 @@ LoopTop:
|
|||
dec [Counter]
|
||||
jnz LoopTop
|
||||
call ZTimerOff
|
||||
</pre><!-- END CODE //-->
|
||||
</pre>
|
||||
|
||||
<p>I’d like to make a brief aside concerning code optimization in the listings in this book. Throughout this book I’ve modeled the sample code after working code so that the timing results are applicable to real-world programming. In Listings 4.1 and 4.2, for example, I could have shown a still greater advantage for byte-sized operands simply by performing 1,000 <b>DEC</b> instructions in a row, with no branching at all. However, <b>DEC</b> instructions don’t exist in a vacuum, so in the listings I used code that both decremented the counter and tested the result. The difference is that between decrementing a memory location (simply an instruction) and using a loop counter (a functional instruction sequence). If you come across code in this book that seems less than optimal, it’s simply due to my desire to provide code that’s relevant to real programming problems. On the other hand, optimal code is an elusive thing indeed; by no means should you assume that the code in this book is ideal! Examine it, question it, and improve upon it, for an inquisitive, skeptical mind is an important part of the Zen of assembly optimization.</p>
|
||||
|
||||
|
|
@ -136,10 +127,6 @@ LoopTop:
|
|||
|
||||
<div align="center">
|
||||
Graphics Programming Black Book © 2001 Michael Abrash
|
||||
</div><!-- all of the reference materials (books) have the footer and subfoot reveresed -->
|
||||
<!-- reference_subfoot = footer -->
|
||||
<!-- reference_footer = subfoot -->
|
||||
<!-- BEGIN SUB FOOTER -->
|
||||
<!-- END FOOTER -->
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
41
04-03.html
41
04-03.html
|
|
@ -10,16 +10,9 @@
|
|||
<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: In the Lair of the Cycle-Eaters</title><!-- HEADER -->
|
||||
<!-- Empty Reference Subhead -->
|
||||
<!--ISBN=1576101746//-->
|
||||
<!--TITLE=Michael Abrash's Graphics Programming Black Book Special Edition//-->
|
||||
<!--AUTHOR=Michael Abrash//-->
|
||||
<!--PUBLISHER=The Coriolis Group, Inc.//-->
|
||||
<title>Michael Abrash's Graphics Programming Black Book Special Edition: In the Lair of the Cycle-Eaters</title>
|
||||
<meta name="chapter" content="04" />
|
||||
<meta name="pages" content="084-087" />
|
||||
<!--UNASSIGNED1//-->
|
||||
<!--UNASSIGNED2//-->
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
|
@ -37,22 +30,22 @@
|
|||
|
||||
<p><br /></p>
|
||||
|
||||
<p>Back to the 8-bit bus cycle-eater. As I’ve said, in 8088 work you should strive to use byte-sized memory variables whenever possible. That does <i>not</i> mean that you should use 2 byte-sized memory accesses to manipulate a word-sized memory variable in preference to 1 word-sized memory access, as, for instance,</p><!-- CODE SNIP //-->
|
||||
<p>Back to the 8-bit bus cycle-eater. As I’ve said, in 8088 work you should strive to use byte-sized memory variables whenever possible. That does <i>not</i> mean that you should use 2 byte-sized memory accesses to manipulate a word-sized memory variable in preference to 1 word-sized memory access, as, for instance,</p>
|
||||
<pre>
|
||||
mov dl,byte ptr [MemVar]
|
||||
mov dh,byte ptr [MemVar+1]
|
||||
</pre><!-- END CODE SNIP //-->
|
||||
</pre>
|
||||
|
||||
<p>versus:</p><!-- CODE SNIP //-->
|
||||
<p>versus:</p>
|
||||
<pre>
|
||||
mov dx,word ptr [MemVar]
|
||||
</pre><!-- END CODE SNIP //-->
|
||||
</pre>
|
||||
|
||||
<p>Recall that every access to a memory byte takes at least 4 cycles; that limitation is built right into the 8088. The 8088 is also built so that the second byte-sized memory access to a 16-bit memory variable takes just those 4 cycles and no more. There’s no way you can manipulate the second byte of a word-sized memory variable faster with a second separate byte-sized instruction in less than 4 cycles. As a matter of fact, you’re bound to access that second byte much more slowly with a separate instruction, thanks to the overhead of instruction fetching and execution, address calculation, and the like.</p>
|
||||
|
||||
<p>For example, consider Listing 4.3, which performs 1,000 word-sized reads from memory. This code runs in 3.77 µs per word read on a 4.77 MHz 8088. That’s 45 percent faster than the 5.49 µs per word read of Listing 4.4, which reads the same 1,000 words as Listing 4.3 but does so with 2,000 byte-sized reads. Both listings perform exactly the same number of memory accesses—2,000 accesses, each byte-sized, as all 8088 memory accesses must be. (Remember that the Bus Interface Unit must perform two byte-sized memory accesses in order to handle a word-sized memory operand.) However, Listing 4.3 is considerably faster because it expends only 4 additional cycles to read the second byte of each word, while Listing 4.4 performs a second <b>LODSB,</b> requiring 13 cycles, to read the second byte of each word.</p>
|
||||
|
||||
<p><b>LISTING 4.3 LST4-3.ASM</b></p><!-- CODE SNIP //-->
|
||||
<p><b>LISTING 4.3 LST4-3.ASM</b></p>
|
||||
<pre>
|
||||
; Measures the performance of reading 1,000 words
|
||||
; from memory with 1,000 word-sized accesses.
|
||||
|
|
@ -62,9 +55,9 @@ mov dx,word ptr [MemVar]
|
|||
call ZTimerOn
|
||||
rep lodsw
|
||||
call ZTimerOff
|
||||
</pre><!-- END CODE SNIP //-->
|
||||
</pre>
|
||||
|
||||
<p><b>LISTING 4.4 LST4-4.ASM</b></p><!-- CODE SNIP //-->
|
||||
<p><b>LISTING 4.4 LST4-4.ASM</b></p>
|
||||
<pre>
|
||||
; Measures the performance of reading 1000 words
|
||||
; from memory with 2,000 byte-sized accesses.
|
||||
|
|
@ -74,7 +67,7 @@ mov dx,word ptr [MemVar]
|
|||
call ZTimerOn
|
||||
rep lodsb
|
||||
call ZTimerOff
|
||||
</pre><!-- END CODE SNIP //-->
|
||||
</pre>
|
||||
|
||||
<p>In short, if you must perform a 16-bit memory access, let the 8088 break the access into two byte-sized accesses for you. The 8088 is more efficient at that task than your code can possibly be.</p>
|
||||
|
||||
|
|
@ -86,7 +79,7 @@ mov dx,word ptr [MemVar]
|
|||
|
||||
<p>Yes and no. It’s true that in general we know approximately how much longer a given instruction will take to execute with a word-sized memory operand than with a byte-sized operand, although the dynamic RAM refresh and wait state cycle-eaters (which I’ll cover a little later) can raise the cost of the 8-bit bus cycle-eater considerably. However, <i>all</i> word-sized memory accesses lose 4 cycles to the 8-bit bus cycle-eater, and there’s one sort of word-sized memory access we haven’t discussed yet: instruction fetching. The ugliest manifestation of the 8-bit bus cycle-eater is in fact the prefetch queue cycle-eater.</p>
|
||||
|
||||
<h3><a id="Heading9"></a>The Prefetch Queue Cycle-Eater</h3>
|
||||
<h3 id="Heading9">The Prefetch Queue Cycle-Eater</h3>
|
||||
|
||||
<p>In an 8088 context, here’s the prefetch queue cycle-eater in a nutshell: The 8088’s 8-bit external data bus keeps the Bus Interface Unit from fetching instruction bytes as fast as the 16-bit Execution Unit can execute them, so the Execution Unit often lies idle while waiting for the next instruction byte to be fetched.</p>
|
||||
|
||||
|
|
@ -96,19 +89,19 @@ mov dx,word ptr [MemVar]
|
|||
|
||||
<p>Clearly, then, the prefetch queue cycle-eater is nothing more than one aspect of the 8-bit bus cycle-eater. 8088 code often runs at less than the Execution Unit’s maximum speed because the 8-bit data bus can’t keep up with the demand for instruction bytes. That’s straightforward enough—so why all the fuss about the prefetch queue cycle-eater?</p>
|
||||
|
||||
<p>What makes the prefetch queue cycle-eater tricky is that it’s undocumented and unpredictable. That is, with a word-sized memory access, such as</p><!-- CODE SNIP //-->
|
||||
<p>What makes the prefetch queue cycle-eater tricky is that it’s undocumented and unpredictable. That is, with a word-sized memory access, such as</p>
|
||||
<pre>
|
||||
mov [bx],ax
|
||||
</pre><!-- END CODE SNIP //-->
|
||||
</pre>
|
||||
|
||||
<p>it’s well-documented that an extra 4 cycles will always be required to write the upper byte of AX to memory. Not so with the prefetch queue cycle-eater lurking nearby. For instance, the instructions</p><!-- CODE SNIP //-->
|
||||
<p>it’s well-documented that an extra 4 cycles will always be required to write the upper byte of AX to memory. Not so with the prefetch queue cycle-eater lurking nearby. For instance, the instructions</p>
|
||||
<pre>
|
||||
shr ax,1
|
||||
shr ax,1
|
||||
shr ax,1
|
||||
shr ax,1
|
||||
shr ax,1
|
||||
</pre><!-- END CODE SNIP //-->
|
||||
</pre>
|
||||
|
||||
<p>should execute in 10 cycles, since each <b>SHR</b> takes 2 cycles to execute, according to Intel’s specifications. Those specifications contain Intel’s official instruction execution times, but in this case—and in many others—the specifications are drastically wrong. Why? Because they describe execution time <i>once an instruction reaches the prefetch queue.</i> They say nothing about whether a given instruction will be in the prefetch queue when it’s time for that instruction to run, or how long it will take that instruction to reach the prefetch queue if it’s not there already. Thanks to the low performance of the 8088’s external data bus, that’s a glaring omission—but, alas, an unavoidable one. Let’s look at why the official execution times are wrong, and why that can’t be helped.</p>
|
||||
|
||||
|
|
@ -129,10 +122,6 @@ shr ax,1
|
|||
|
||||
<div align="center">
|
||||
Graphics Programming Black Book © 2001 Michael Abrash
|
||||
</div><!-- all of the reference materials (books) have the footer and subfoot reveresed -->
|
||||
<!-- reference_subfoot = footer -->
|
||||
<!-- reference_footer = subfoot -->
|
||||
<!-- BEGIN SUB FOOTER -->
|
||||
<!-- END FOOTER -->
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
32
04-04.html
32
04-04.html
|
|
@ -10,16 +10,9 @@
|
|||
<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: In the Lair of the Cycle-Eaters</title><!-- HEADER -->
|
||||
<!-- Empty Reference Subhead -->
|
||||
<!--ISBN=1576101746//-->
|
||||
<!--TITLE=Michael Abrash's Graphics Programming Black Book Special Edition//-->
|
||||
<!--AUTHOR=Michael Abrash//-->
|
||||
<!--PUBLISHER=The Coriolis Group, Inc.//-->
|
||||
<title>Michael Abrash's Graphics Programming Black Book Special Edition: In the Lair of the Cycle-Eaters</title>
|
||||
<meta name="chapter" content="04" />
|
||||
<meta name="pages" content="087-090" />
|
||||
<!--UNASSIGNED1//-->
|
||||
<!--UNASSIGNED2//-->
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
|
@ -37,7 +30,7 @@
|
|||
|
||||
<p><br /></p>
|
||||
|
||||
<h4 align="left"><a id="Heading10"></a>Official Execution Times Are Only Part of the Story</h4>
|
||||
<h4 align="left" id="Heading10">Official Execution Times Are Only Part of the Story</h4>
|
||||
|
||||
<p>The sequence of 5 <b>SHR</b> instructions in the last example is 10 bytes long. That means that it can never execute in less than 24 cycles even if the 4-byte prefetch queue is full when it starts, since 6 instruction bytes would still remain to be fetched, at 4 cycles per fetch. If the prefetch queue is empty at the start, the sequence <i>could</i> take 40 cycles. In short, thanks to instruction fetching, the code won’t run at its documented speed, and could take up to four times longer than it is supposed to.</p>
|
||||
|
||||
|
|
@ -55,7 +48,7 @@
|
|||
|
||||
<p>So now you know why the official instruction execution times are often wrong, and why Intel can’t provide better specifications. You also know now why it is that you must time your code if you want to know how fast it really is.</p>
|
||||
|
||||
<h4 align="left"><a id="Heading11"></a>There Is No Such Beast as a True Instruction Execution Time</h4>
|
||||
<h4 align="left" id="Heading11">There Is No Such Beast as a True Instruction Execution Time</h4>
|
||||
|
||||
<p>The effect of the code preceding an instruction on the execution time of that instruction makes the Zen timer trickier to use than you might expect, and complicates the interpretation of the results reported by the Zen timer. For one thing, the Zen timer is best used to time code sequences that are more than a few instructions long; below 10µs or so, prefetch queue effects and the limited resolution of the clock driving the timer can cause problems.</p>
|
||||
|
||||
|
|
@ -65,7 +58,7 @@
|
|||
|
||||
<p>For example, consider the code in Listings 4.5 and 4.6. Listing 4.5 shows our familiar <b>SHR</b> case. Here, because the prefetch queue is always empty, execution time should work out to about 4 cycles per byte, or 8 cycles per <b>SHR,</b> as shown in Figure 4.3. (Figure 4.3 illustrates the relationship between instruction fetching and execution in a simplified way, and is not intended to show the exact timings of 8088 operations.) That’s quite a contrast to the official 2-cycle execution time of <b>SHR</b>. In fact, the Zen timer reports that Listing 4.5 executes in 1.81µs per byte, or slightly <i>more</i> than 4 cycles per byte. (The extra time is the result of the dynamic RAM refresh cycle-eater, which we’ll discuss shortly.) Going by Listing 4.5, we would conclude that the “true” execution time of <b>SHR</b> is 8.64 cycles.</p>
|
||||
|
||||
<p><b>LISTING 4.5 LST4-5.ASM</b></p><!-- CODE //-->
|
||||
<p><b>LISTING 4.5 LST4-5.ASM</b></p>
|
||||
<pre>
|
||||
; Measures the performance of 1,000 SHR instructions
|
||||
; in a row. Since SHR executes in 2 cycles but is
|
||||
|
|
@ -78,9 +71,9 @@
|
|||
shr ax,1
|
||||
endm
|
||||
call ZTimerOff
|
||||
</pre><!-- END CODE //-->
|
||||
</pre>
|
||||
|
||||
<p><b>LISTING 4.6 LST4-6.ASM</b></p><!-- CODE //-->
|
||||
<p><b>LISTING 4.6 LST4-6.ASM</b></p>
|
||||
<pre>
|
||||
; Measures the performance of 1,000 MUL/SHR instruction
|
||||
; pairs in a row. The lengthy execution time of MUL
|
||||
|
|
@ -94,11 +87,10 @@
|
|||
shr ax,1
|
||||
endm
|
||||
call ZTimerOff
|
||||
</pre><!-- END CODE //-->
|
||||
</pre>
|
||||
|
||||
<p><a id="Fig3"><!-- </A><A HREF="javascript:displayWindow('images/04-03.jpg',414,337 )"> --><img src="images/04-03.jpg" /><br />
|
||||
<!-- </A>
|
||||
<BR><A HREF="javascript:displayWindow('images/04-03.jpg',414,337)"> --><b>Figure 4.3</b></a> <i>Execution and instruction prefetching sequence for Listing 4.5.</i></p>
|
||||
<p><a id="Fig3"><img src="images/04-03.jpg" /><br />
|
||||
<b>Figure 4.3</b></a> <i>Execution and instruction prefetching sequence for Listing 4.5.</i></p>
|
||||
|
||||
<p>Now let’s examine Listing 4.6. Here each <b>SHR</b> follows a <b>MUL</b> instruction. Since <b>MUL</b> instructions take so long to execute that the prefetch queue is always full when they finish, each <b>SHR</b> should be ready and waiting in the prefetch queue when the preceding <b>MUL</b> ends. As a result, we’d expect that each <b>SHR</b> would execute in 2 cycles; together with the 118-cycle execution time of multiplying 0 times 0, the total execution time should come to 120 cycles per <b>SHR/MUL</b> pair, as shown in Figure 4.4. And, by God, when we run Listing 4.6 we get an execution time of 25.14 µs per <b>SHR/MUL</b> pair, or <i>exactly</i> 120 cycles! According to these results, the “true” execution time of <b>SHR</b> would seem to be 2 cycles, quite a change from the conclusion we drew from Listing 4.5.</p>
|
||||
|
||||
|
|
@ -121,10 +113,6 @@
|
|||
|
||||
<div align="center">
|
||||
Graphics Programming Black Book © 2001 Michael Abrash
|
||||
</div><!-- all of the reference materials (books) have the footer and subfoot reveresed -->
|
||||
<!-- reference_subfoot = footer -->
|
||||
<!-- reference_footer = subfoot -->
|
||||
<!-- BEGIN SUB FOOTER -->
|
||||
<!-- END FOOTER -->
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
32
04-05.html
32
04-05.html
|
|
@ -10,16 +10,9 @@
|
|||
<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: In the Lair of the Cycle-Eaters</title><!-- HEADER -->
|
||||
<!-- Empty Reference Subhead -->
|
||||
<!--ISBN=1576101746//-->
|
||||
<!--TITLE=Michael Abrash's Graphics Programming Black Book Special Edition//-->
|
||||
<!--AUTHOR=Michael Abrash//-->
|
||||
<!--PUBLISHER=The Coriolis Group, Inc.//-->
|
||||
<title>Michael Abrash's Graphics Programming Black Book Special Edition: In the Lair of the Cycle-Eaters</title>
|
||||
<meta name="chapter" content="04" />
|
||||
<meta name="pages" content="090-093" />
|
||||
<!--UNASSIGNED1//-->
|
||||
<!--UNASSIGNED2//-->
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
|
@ -41,11 +34,10 @@
|
|||
|
||||
<p>The truth is that it never hurts performance to reduce either the cycle count or the byte count of a given bit of code, but there’s no guarantee that one or the other will improve performance either. For example, consider Listing 4.7, which consists of a series of 4-cycle, 2-byte <b>MOV AL,0</b> instructions, and which executes at the rate of 1.81 µs per instruction. Now consider Listing 4.8, which replaces the 4-cycle <b>MOV AL,0</b> with the 3-cycle (but still 2-byte) <b>SUB AL,AL,</b> Despite its 1-cycle-per-instruction advantage, Listing 4.8 runs at exactly the same speed as Listing 4.7. The reason: Both instructions are 2 bytes long, and in both cases it is the 8-cycle instruction fetch time, not the 3 or 4-cycle Execution Unit execution time, that limits performance.</p>
|
||||
|
||||
<p><a id="Fig4"><!-- </A><A HREF="javascript:displayWindow('images/04-04.jpg',410,449 )"> --><img src="images/04-04.jpg" /><br />
|
||||
<!-- </A>
|
||||
<BR><A HREF="javascript:displayWindow('images/04-04.jpg',410,449)"> --><b>Figure 4.4</b></a> <i>Execution and instruction prefetching sequence for Listing 4.6.</i></p>
|
||||
<p><a id="Fig4"><img src="images/04-04.jpg" /><br />
|
||||
<b>Figure 4.4</b></a> <i>Execution and instruction prefetching sequence for Listing 4.6.</i></p>
|
||||
|
||||
<p><b>LISTING 4.7 LST4-7.ASM</b></p><!-- CODE //-->
|
||||
<p><b>LISTING 4.7 LST4-7.ASM</b></p>
|
||||
<pre>
|
||||
; Measures the performance of repeated MOV AL,0 instructions,
|
||||
; which take 4 cycles each according to Intel's official
|
||||
|
|
@ -57,9 +49,9 @@
|
|||
mov al,0
|
||||
endm
|
||||
call ZTimerOff
|
||||
</pre><!-- END CODE //-->
|
||||
</pre>
|
||||
|
||||
<p><b>LISTING 4.8 LST4-8.ASM</b></p><!-- CODE //-->
|
||||
<p><b>LISTING 4.8 LST4-8.ASM</b></p>
|
||||
<pre>
|
||||
; Measures the performance of repeated SUB AL,AL instructions,
|
||||
; which take 3 cycles each according to Intel's official
|
||||
|
|
@ -71,7 +63,7 @@
|
|||
sub al,al
|
||||
endm
|
||||
call ZTimerOff
|
||||
</pre><!-- END CODE //-->
|
||||
</pre>
|
||||
|
||||
<p>As you can see, it’s easy to be drawn into thinking you’re saving cycles when you’re not. You can only improve the performance of a specific bit of code by reducing the factor—either instruction fetch time or execution time, or sometimes a mix of the two—that’s limiting the performance of that code.</p>
|
||||
|
||||
|
|
@ -87,7 +79,7 @@
|
|||
|
||||
<p>What we <i>really</i> want is to know how long useful working code takes to run, not how long a single instruction takes, and the Zen timer gives us the tool we need to gather that information. Granted, it would be easier if we could just add up neatly documented instruction execution times—but that’s not going to happen. Without actually measuring the performance of a given code sequence, you simply don’t know how fast it is. For crying out loud, even the people who <i>designed</i> the 8088 at Intel couldn’t tell you exactly how quickly a given 8088 code sequence executes on the PC just by looking at it! Get used to the idea that execution times are only meaningful in context, learn the rules of thumb in this book, and use the Zen timer to measure your code.</p>
|
||||
|
||||
<h4 align="left"><a id="Heading12"></a>Approximating Overall Execution Times</h4>
|
||||
<h4 align="left" id="Heading12">Approximating Overall Execution Times</h4>
|
||||
|
||||
<p>Don’t think that because overall instruction execution time is determined by both instruction fetch time and Execution Unit execution time, the two times should be added together when estimating performance. For example, practically speaking, each <b>SHR</b> in Listing 4.5 does not take 8 cycles of instruction fetch time plus 2 cycles of Execution Unit execution time to execute. Figure 4.3 shows that while a given <b>SHR</b> is executing, the fetch of the next <b>SHR</b> is starting, and since the two operations are overlapped for 2 cycles, there’s no sense in charging the time to both instructions. You could think of the extra instruction fetch time for <b>SHR</b> in Listing 4.5 as being 6 cycles, which yields an overall execution time of 8 cycles when added to the 2 cycles of Execution Unit execution time.</p>
|
||||
|
||||
|
|
@ -95,7 +87,7 @@
|
|||
|
||||
<p>As a working definition, we’ll consider the execution time of a given instruction in a particular context to start when the first byte of the instruction is sent to the Execution Unit and end when the first byte of the next instruction is sent to the EU.</p>
|
||||
|
||||
<h4 align="left"><a id="Heading13"></a>What to Do about the Prefetch Queue Cycle-Eater?</h4>
|
||||
<h4 align="left" id="Heading13">What to Do about the Prefetch Queue Cycle-Eater?</h4>
|
||||
|
||||
<p>Reducing the impact of the prefetch queue cycle-eater is one of the overriding principles of high-performance assembly code. How can you do this? One effective technique is to minimize access to memory operands, since such accesses compete with instruction fetching for precious memory accesses. You can also greatly reduce instruction fetch time simply by your choice of instructions: <i>Keep your instructions short.</i> Less time is required to fetch instructions that are 1 or 2 bytes long than instructions that are 5 or 6 bytes long. Reduced instruction fetching lowers minimum execution time (minimum execution time is 4 cycles times the number of instruction bytes) and often leads to faster overall execution.</p>
|
||||
|
||||
|
|
@ -118,10 +110,6 @@
|
|||
|
||||
<div align="center">
|
||||
Graphics Programming Black Book © 2001 Michael Abrash
|
||||
</div><!-- all of the reference materials (books) have the footer and subfoot reveresed -->
|
||||
<!-- reference_subfoot = footer -->
|
||||
<!-- reference_footer = subfoot -->
|
||||
<!-- BEGIN SUB FOOTER -->
|
||||
<!-- END FOOTER -->
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
26
04-06.html
26
04-06.html
|
|
@ -10,16 +10,9 @@
|
|||
<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: In the Lair of the Cycle-Eaters</title><!-- HEADER -->
|
||||
<!-- Empty Reference Subhead -->
|
||||
<!--ISBN=1576101746//-->
|
||||
<!--TITLE=Michael Abrash's Graphics Programming Black Book Special Edition//-->
|
||||
<!--AUTHOR=Michael Abrash//-->
|
||||
<!--PUBLISHER=The Coriolis Group, Inc.//-->
|
||||
<title>Michael Abrash's Graphics Programming Black Book Special Edition: In the Lair of the Cycle-Eaters</title>
|
||||
<meta name="chapter" content="04" />
|
||||
<meta name="pages" content="094-097" />
|
||||
<!--UNASSIGNED1//-->
|
||||
<!--UNASSIGNED2//-->
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
|
@ -41,7 +34,7 @@
|
|||
|
||||
<p>All in all, writing good assembler code is as much an art as a science. As a result, you should follow the rules of thumb described here—and then time your code to see how fast it really is. You should experiment freely, but always remember that actual, measured performance is the bottom line.</p>
|
||||
|
||||
<h4 align="left"><a id="Heading14"></a>Holding Up the 8088</h4>
|
||||
<h4 align="left" id="Heading14">Holding Up the 8088</h4>
|
||||
|
||||
<p>In this chapter I’ve taken you further and further into the depths of the PC, telling you again and again that you must understand the computer at the lowest possible level in order to write good code. At this point, you may well wonder, “Have we gotten low enough?”</p>
|
||||
|
||||
|
|
@ -53,7 +46,7 @@
|
|||
|
||||
<p>Let’s start with DRAM refresh, which affects the performance of every program that runs on the PC.</p>
|
||||
|
||||
<h3><a id="Heading15"></a>Dynamic RAM Refresh: The Invisible Hand</h3>
|
||||
<h3 id="Heading15">Dynamic RAM Refresh: The Invisible Hand</h3>
|
||||
|
||||
<p>Dynamic RAM (DRAM) refresh is sort of an act of God. By that I mean that DRAM refresh invisibly and inexorably steals a certain fraction of all available memory access time from your programs, when they are accessing memory for code and data. (When they are accessing cache on more recent processors, theoretically the DRAM refresh cycle-eater doesn’t come into play, but there are other cycle-eaters waiting to prey on cache-bound programs.) While you <i>could</i> stop DRAM refresh, you wouldn’t want to since that would be a sure prescription for crashing your computer. In the end, thanks to DRAM refresh, almost all code runs a bit slower on the PC than it otherwise would, and that’s that.</p>
|
||||
|
||||
|
|
@ -61,7 +54,7 @@
|
|||
|
||||
<p>All of the PC’s system memory consists of DRAM chips. Each DRAM chip in the PC must be completely refreshed about once every four milliseconds in order to ensure the integrity of the data it stores. Obviously, it’s highly desirable that the memory in the PC retain the correct data indefinitely, so each DRAM chip in the PC <i>must</i> always be refreshed within 4 µs of the last refresh. Since there’s no guarantee that a given program will access each and every DRAM block once every 4 µs, the PC contains special circuitry and programming for providing DRAM refresh.</p>
|
||||
|
||||
<h4 align="left"><a id="Heading16"></a>How DRAM Refresh Works in the PC</h4>
|
||||
<h4 align="left" id="Heading16">How DRAM Refresh Works in the PC</h4>
|
||||
|
||||
<p>On the original 8088-based IBM PC, timer 1 of the 8253 timer chip is programmed at power-up to generate a signal once every 72 cycles, or once every 15.08µs. That signal goes to channel 0 of the 8237 DMA controller, which requests the bus from the 8088 upon receiving the signal. (DMA stands for <i>direct memory access,</i> the ability of a device other than the 8088 to control the bus and access memory directly, without any help from the 8088.) As soon as the 8088 is between memory accesses, it gives control of the bus to the 8237, which in conjunction with special circuitry on the PC’s motherboard then performs a single 4-cycle read access to 1 of 256 possible addresses, advancing to the next address on each successive access. (The read access is only for the purpose of refreshing the DRAM; the data that is read isn’t used.)</p>
|
||||
|
||||
|
|
@ -69,9 +62,8 @@
|
|||
|
||||
<p>Don’t sweat the details here. The important point is this: For at least 4 out of every 72 cycles, the original PC’s bus is given over to DRAM refresh and is not available to the 8088, as shown in Figure 4.5. That means that as much as 5.56 percent of the PC’s already inadequate bus capacity is lost. However, DRAM refresh doesn’t necessarily stop the 8088 in its tracks for 4 cycles. The Execution Unit of the 8088 can keep processing while DRAM refresh is occurring, unless the EU needs to access memory. Consequently, DRAM refresh can slow code performance anywhere from 0 percent to 5.56 percent (and actually a bit more, as we'll see shortly), depending on the extent to which DRAM refresh occupies cycles during which the 8088 would otherwise be accessing memory.</p>
|
||||
|
||||
<p><a id="Fig5"><!-- </A><A HREF="javascript:displayWindow('images/04-05.jpg',407,339 )"> --><img src="images/04-05.jpg" /><br />
|
||||
<!-- </A>
|
||||
<BR><A HREF="javascript:displayWindow('images/04-05.jpg',407,339)"> --><b>Figure 4.5</b></a> <i>The PC bus dynamic RAM (DRAM) refresh.</i></p>
|
||||
<p><a id="Fig5"><img src="images/04-05.jpg" /><br />
|
||||
<b>Figure 4.5</b></a> <i>The PC bus dynamic RAM (DRAM) refresh.</i></p>
|
||||
|
||||
<p><br /></p>
|
||||
|
||||
|
|
@ -90,10 +82,6 @@
|
|||
|
||||
<div align="center">
|
||||
Graphics Programming Black Book © 2001 Michael Abrash
|
||||
</div><!-- all of the reference materials (books) have the footer and subfoot reveresed -->
|
||||
<!-- reference_subfoot = footer -->
|
||||
<!-- reference_footer = subfoot -->
|
||||
<!-- BEGIN SUB FOOTER -->
|
||||
<!-- END FOOTER -->
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
29
04-07.html
29
04-07.html
|
|
@ -10,16 +10,9 @@
|
|||
<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: In the Lair of the Cycle-Eaters</title><!-- HEADER -->
|
||||
<!-- Empty Reference Subhead -->
|
||||
<!--ISBN=1576101746//-->
|
||||
<!--TITLE=Michael Abrash's Graphics Programming Black Book Special Edition//-->
|
||||
<!--AUTHOR=Michael Abrash//-->
|
||||
<!--PUBLISHER=The Coriolis Group, Inc.//-->
|
||||
<title>Michael Abrash's Graphics Programming Black Book Special Edition: In the Lair of the Cycle-Eaters</title>
|
||||
<meta name="chapter" content="04" />
|
||||
<meta name="pages" content="097-099" />
|
||||
<!--UNASSIGNED1//-->
|
||||
<!--UNASSIGNED2//-->
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
|
@ -37,11 +30,11 @@
|
|||
|
||||
<p><br /></p>
|
||||
|
||||
<h4 align="left"><a id="Heading17"></a>The Impact of DRAM Refresh</h4>
|
||||
<h4 align="left" id="Heading17">The Impact of DRAM Refresh</h4>
|
||||
|
||||
<p>Let’s look at examples from opposite ends of the spectrum in terms of the impact of DRAM refresh on code performance. First, consider the series of <b>MUL</b> instructions in Listing 4.9. Since a 16-bit <b>MUL</b> on the 8088 executes in between 118 and 133 cycles and is only 2 bytes long, there should be plenty of time for the prefetch queue to fill after each instruction, even after DRAM refresh has taken its slice of memory access time. Consequently, the prefetch queue should be able to keep the Execution Unit well-supplied with instruction bytes at all times. Since Listing 4.9 uses no memory operands, the Execution Unit should never have to wait for data from memory, and DRAM refresh should have no impact on performance. (Remember that the Execution Unit can operate normally during DRAM refreshes so long as it doesn’t need to request a memory access from the Bus Interface Unit.)</p>
|
||||
|
||||
<p><b>LISTING 4.9 LST4-9.ASM</b></p><!-- CODE //-->
|
||||
<p><b>LISTING 4.9 LST4-9.ASM</b></p>
|
||||
<pre>
|
||||
; Measures the performance of repeated MUL instructions,
|
||||
; which allow the prefetch queue to be full at all times,
|
||||
|
|
@ -54,13 +47,13 @@
|
|||
mul ax
|
||||
endm
|
||||
call ZTimerOff
|
||||
</pre><!-- END CODE //-->
|
||||
</pre>
|
||||
|
||||
<p>Running Listing 4.9, we find that each <b>MUL</b> executes in 24.72 µs, or exactly 118 cycles. Since that’s the shortest time in which <b>MUL</b> can execute, we can see that no performance is lost to DRAM refresh. Listing 4.9 clearly illustrates that DRAM refresh only affects code performance when a DRAM refresh forces the Execution Unit of the 8088 to wait for a memory access.</p>
|
||||
|
||||
<p>Now let’s look at the series of <b>SHR</b> instructions shown in Listing 4.10. Since <b>SHR</b> executes in 2 cycles but is 2 bytes long, the prefetch queue should be empty while Listing 4.10 executes, with the 8088 prefetching instruction bytes non-stop. As a result, the time per instruction of Listing 4.10 should precisely reflect the time required to fetch the instruction bytes.</p>
|
||||
|
||||
<p><b>LISTING 4.10 LST4-10.ASM</b></p><!-- CODE //-->
|
||||
<p><b>LISTING 4.10 LST4-10.ASM</b></p>
|
||||
<pre>
|
||||
; Measures the performance of repeated SHR instructions,
|
||||
; which empty the prefetch queue, to demonstrate the
|
||||
|
|
@ -71,7 +64,7 @@
|
|||
shr ax,1
|
||||
endm
|
||||
call ZTimerOff
|
||||
</pre><!-- END CODE //-->
|
||||
</pre>
|
||||
|
||||
<p>Since 4 cycles are required to read each instruction byte, we’d expect each <b>SHR</b> to execute in 8 cycles, or 1.676 µs, if there were no DRAM refresh. In fact, each <b>SHR</b> in Listing 4.10 executes in 1.81 µs, indicating that DRAM refresh is taking 7.4 percent of the program’s execution time. That’s nearly 2 percent more than our worst-case estimate of the loss to DRAM refresh overhead! In fact, the result indicates that DRAM refresh is stealing not 4, but 5.33 cycles out of every 72 cycles. How can this be?</p>
|
||||
|
||||
|
|
@ -79,7 +72,7 @@
|
|||
|
||||
<p>Which of the two cases we’ve examined reflects reality? While either case <i>can</i> happen, the latter case—significant performance reduction, ranging as high as 8.33 percent—is far more likely to occur. This is especially true for high-performance assembly code, which uses fast instructions that tend to cause non-stop instruction fetching.</p>
|
||||
|
||||
<h4 align="left"><a id="Heading18"></a>What to Do About the DRAM Refresh Cycle-Eater?</h4>
|
||||
<h4 align="left" id="Heading18">What to Do About the DRAM Refresh Cycle-Eater?</h4>
|
||||
|
||||
<p><i>Hmmm.</i> When we discovered the prefetch queue cycle-eater, we learned to use short instructions. When we discovered the 8-bit bus cycle-eater, we learned to use byte-sized memory operands whenever possible, and to keep word-sized variables in registers. What can we do to work around the DRAM refresh cycle-eater?</p>
|
||||
|
||||
|
|
@ -91,7 +84,7 @@
|
|||
|
||||
<p>The important thing to understand about DRAM refresh is that it generally slows your code down, and that the extent of that performance reduction can vary considerably and unpredictably, depending on how the DRAM refreshes interact with your code’s pattern of memory accesses. When you use the Zen timer and get a fractional cycle count for the execution time of an instruction, that’s often the DRAM refresh cycle-eater at work. (The display adapter cycleis another possible culprit, and, on 386s and later processors, cache misses and pipeline execution hazards produce this sort of effect as well.) Whenever you get two timing results that differ less or more than they seemingly should, that’s usually DRAM refresh too. Thanks to DRAM refresh, variations of up to 8.33 percent in PC code performance are par for the course.</p>
|
||||
|
||||
<h3><a id="Heading19"></a>Wait States</h3>
|
||||
<h3 id="Heading19">Wait States</h3>
|
||||
|
||||
<p>Wait states are cycles during which a bus access by the CPU to a device on the PC’s bus is temporarily halted by that device while the device gets ready to complete the read or write. Wait states are well and truly the lowest level of code performance. Everything we have discussed (and will discuss)—even DMA accesses—can be affected by wait states.</p>
|
||||
|
||||
|
|
@ -112,10 +105,6 @@
|
|||
|
||||
<div align="center">
|
||||
Graphics Programming Black Book © 2001 Michael Abrash
|
||||
</div><!-- all of the reference materials (books) have the footer and subfoot reveresed -->
|
||||
<!-- reference_subfoot = footer -->
|
||||
<!-- reference_footer = subfoot -->
|
||||
<!-- BEGIN SUB FOOTER -->
|
||||
<!-- END FOOTER -->
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
32
04-08.html
32
04-08.html
|
|
@ -10,16 +10,9 @@
|
|||
<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: In the Lair of the Cycle-Eaters</title><!-- HEADER -->
|
||||
<!-- Empty Reference Subhead -->
|
||||
<!--ISBN=1576101746//-->
|
||||
<!--TITLE=Michael Abrash's Graphics Programming Black Book Special Edition//-->
|
||||
<!--AUTHOR=Michael Abrash//-->
|
||||
<!--PUBLISHER=The Coriolis Group, Inc.//-->
|
||||
<title>Michael Abrash's Graphics Programming Black Book Special Edition: In the Lair of the Cycle-Eaters</title>
|
||||
<meta name="chapter" content="04" />
|
||||
<meta name="pages" content="099-104" />
|
||||
<!--UNASSIGNED1//-->
|
||||
<!--UNASSIGNED2//-->
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
|
@ -45,15 +38,14 @@
|
|||
|
||||
<p>First, let’s learn a bit more about wait states by contrast with DRAM refresh. Unlike DRAM refresh, wait states do not occur on any regularly scheduled basis, and are of no particular duration. Wait states can only occur when an instruction performs a memory or I/O read or write. Both the presence of wait states and the number of wait states inserted on any given bus access are entirely controlled by the device being accessed. When it comes to wait states, the CPU is passive, merely accepting whatever wait states the accessed device chooses to insert during the course of the access. All of this makes perfect sense given that the whole point of the wait state mechanism is to allow a device to stretch out any access to itself for however much time it needs to perform the access.</p>
|
||||
|
||||
<p><a id="Fig6"><!-- </A><A HREF="javascript:displayWindow('images/04-06.jpg',415,242 )"> --><img src="images/04-06.jpg" /><br />
|
||||
<!-- </A>
|
||||
<BR><A HREF="javascript:displayWindow('images/04-06.jpg',415,242)"> --><b>Figure 4.6</b></a> <i>Video wait states inserted by the display adapter.</i></p>
|
||||
<p><a id="Fig6"><img src="images/04-06.jpg" /><br />
|
||||
<b>Figure 4.6</b></a> <i>Video wait states inserted by the display adapter.</i></p>
|
||||
|
||||
<p>As with DRAM refresh, wait states don’t stop the 8088 completely. The Execution Unit can continue processing while wait states are inserted, so long as the EU doesn’t need to perform a bus access. However, in the PC, wait states most often occur when an instruction accesses a memory operand, so in fact the Execution Unit usually is stopped by wait states. (Instruction fetches rarely wait in an 8088-based PC because system memory is zero-wait-state. AT-class memory systems routinely insert 1 or more wait states, however.)</p>
|
||||
|
||||
<p>As it turns out, wait states pose a serious problem in just one area in the PC. While any adapter <i>can</i> insert wait states, in the PC only display adapters do so to the extent that performance is seriously affected.</p>
|
||||
|
||||
<h3><a id="Heading20"></a>The Display Adapter Cycle-Eater</h3>
|
||||
<h3 id="Heading20">The Display Adapter Cycle-Eater</h3>
|
||||
|
||||
<p>Display adapters must serve two masters, and that creates a fundamental performance problem. Master #1 is the circuitry that drives the display screen. This circuitry must constantly read display memory in order to obtain the information used to draw the characters or dots displayed on the screen. Since the screen must be redrawn between 50 and 70 times per second, and since each redraw of the screen can require as many as 36,000 reads of display memory (more in Super VGA modes), master #1 is a demanding master indeed. No matter how demanding master #1 gets, however, its needs must <i>always</i> be met—otherwise the quality of the picture on the screen would suffer.</p>
|
||||
|
||||
|
|
@ -63,17 +55,15 @@
|
|||
|
||||
<p>It turns out that the 8088 CPU has to do a lot of waiting, for three reasons. First, the video circuitry can take as much as about 90 percent of the available display memory access time, as shown in Figure 4.7, leaving as little as about 10 percent of all display memory accesses for the 8088. (These percentages vary considerably among the many EGA and VGA clones.)</p>
|
||||
|
||||
<p><a id="Fig7"><!-- </A><A HREF="javascript:displayWindow('images/04-07.jpg',415,342 )"> --><img src="images/04-07.jpg" /><br />
|
||||
<!-- </A>
|
||||
<BR><A HREF="javascript:displayWindow('images/04-07.jpg',415,342)"> --><b>Figure 4.7</b></a> <i>Allocation of display memory access.</i></p>
|
||||
<p><a id="Fig7"><img src="images/04-07.jpg" /><br />
|
||||
<b>Figure 4.7</b></a> <i>Allocation of display memory access.</i></p>
|
||||
|
||||
<p>Second, because the displayed dots (or <i>pixels,</i> short for “picture elements”) must be drawn on the screen at a constant speed, many display adapters provide memory accesses only at fixed intervals. As a result, time can be lost while the 8088 synchronizes with the start of the next display adapter memory access, even if the video circuitry isn’t accessing display memory at that time, as shown in Figure 4.8.</p>
|
||||
|
||||
<p>Finally, the time it takes a display adapter to complete a memory access is related to the speed of the clock which generates pixels on the screen rather than to the memory access speed of the 8088. Consequently, the time taken for display memory to complete an 8088 read or write access is often longer than the time taken for system memory to complete an access, even if the 8088 lucks into hitting a free display memory access just as it becomes available, again as shown in Figure 4.8. Any or all of the three factors I’ve described can result in wait states, slowing the 8088 and creating the display adapter cycle.</p>
|
||||
|
||||
<p><a id="Fig8"><!-- </A><A HREF="javascript:displayWindow('images/04-08.jpg',417,375 )"> --><img src="images/04-08.jpg" /><br />
|
||||
<!-- </A>
|
||||
<BR><A HREF="javascript:displayWindow('images/04-08.jpg',417,375)"> --><b>Figure 4.8</b></a> <i>Display memory access slots.</i></p>
|
||||
<p><a id="Fig8"><img src="images/04-08.jpg" /><br />
|
||||
<b>Figure 4.8</b></a> <i>Display memory access slots.</i></p>
|
||||
|
||||
<p>If some of this is Greek to you, don’t worry. The important point is that display memory is not very fast compared to normal system memory. How slow is it? <i>Incredibly</i> slow. Remember how slow IBM’s ill-fated PC<i>jr</i>was? In case you’ve forgotten, I’ll refresh your memory: The PC<i>jr</i>was at best only half as fast as the PC. The PC<i>jr</i> had an 8088 running at 4.77 MHz, just like the PC—why do you suppose it was so much slower? I’ll tell you why: <i>All the memory in the PCjr was display memory.</i></p>
|
||||
|
||||
|
|
@ -96,10 +86,6 @@
|
|||
|
||||
<div align="center">
|
||||
Graphics Programming Black Book © 2001 Michael Abrash
|
||||
</div><!-- all of the reference materials (books) have the footer and subfoot reveresed -->
|
||||
<!-- reference_subfoot = footer -->
|
||||
<!-- reference_footer = subfoot -->
|
||||
<!-- BEGIN SUB FOOTER -->
|
||||
<!-- END FOOTER -->
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
25
04-09.html
25
04-09.html
|
|
@ -10,16 +10,9 @@
|
|||
<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: In the Lair of the Cycle-Eaters</title><!-- HEADER -->
|
||||
<!-- Empty Reference Subhead -->
|
||||
<!--ISBN=1576101746//-->
|
||||
<!--TITLE=Michael Abrash's Graphics Programming Black Book Special Edition//-->
|
||||
<!--AUTHOR=Michael Abrash//-->
|
||||
<!--PUBLISHER=The Coriolis Group, Inc.//-->
|
||||
<title>Michael Abrash's Graphics Programming Black Book Special Edition: In the Lair of the Cycle-Eaters</title>
|
||||
<meta name="chapter" content="4" />
|
||||
<meta name="pages" content="104-106" />
|
||||
<!--UNASSIGNED1//-->
|
||||
<!--UNASSIGNED2//-->
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
|
@ -39,7 +32,7 @@
|
|||
|
||||
<p>The answer varies considerably depending on what display adapter and what display mode we’re talking about. The display adapter cycle-eater is worst with the Enhanced Graphics Adapter (EGA) and the original Video Graphics Array (VGA). (Many VGAs, especially newer ones, insert many fewer wait states than IBM’s original VGA. On the other hand, Super VGAs have more bytes of display memory to be accessed in high-resolution mode.) While the Color/Graphics Adapter (CGA), Monochrome Display Adapter (MDA), and Hercules Graphics Card (HGC) all suffer from the display adapter cycle-eater as well, they suffer to a lesser degree. Since the VGA represents the base standard for PC graphics now and for the foreseeable future, and since it is the hardest graphics adapter to wring performance from, we’ll restrict our discussion to the VGA (and its close relative, the EGA) for the remainder of this chapter.</p>
|
||||
|
||||
<h4 align="left"><a id="Heading21"></a>The Impact of the Display Adapter Cycle-Eater</h4>
|
||||
<h4 align="left" id="Heading21">The Impact of the Display Adapter Cycle-Eater</h4>
|
||||
|
||||
<p>Even on the EGA and VGA, the effect of the display adapter cycle-eater depends on the display mode selected. In text mode, the display adapter cycle-eater is rarely a major factor. It’s not that the cycle-eater isn’t present; however, a mere 4,000 bytes control the entire text mode display, and even with the display adapter cycle-eater it just doesn’t take that long to manipulate 4,000 bytes. Even if the display adapter cycle-eater were to cause the 8088 to take as much as 5µs per display memory access—more than five times normal—it would still take only 4,000x 2x 5µs, or 40 µs, to read and write every byte of display memory. That’s a lot of time as measured in 8088 cycles, but it’s less than the blink of an eye in human time, and video performance only matters in human time. After all, the whole point of drawing graphics is to convey visual information, and if that information can be presented faster than the eye can see, that is by definition fast enough.</p>
|
||||
|
||||
|
|
@ -51,7 +44,7 @@
|
|||
|
||||
<p>That sounds pretty serious, but we did make an unfounded assumption about memory access speed. Let’s get some hard numbers. Listing 4.11 accesses display memory at the 8088’s maximum speed, by way of a <b>REP MOVSW</b> with display memory as both source and destination. The code in Listing 4.11 executes in 3.18 µs per access to display memory—not as long as we had assumed, but a long time nonetheless.</p>
|
||||
|
||||
<p><b>LISTING 4.11 LST4-11.ASM</b></p><!-- CODE //-->
|
||||
<p><b>LISTING 4.11 LST4-11.ASM</b></p>
|
||||
<pre>
|
||||
; Times speed of memory access to Enhanced Graphics
|
||||
; Adapter graphics mode display memory at A000:0000.
|
||||
|
|
@ -81,11 +74,11 @@
|
|||
;
|
||||
mov ax,0003h
|
||||
int 10h ;return to text mode
|
||||
</pre><!-- END CODE //-->
|
||||
</pre>
|
||||
|
||||
<p>For comparison, let’s see how long the same code takes when accessing normal system RAM instead of display memory. The code in Listing 4.12, which performs a <b>REP MOVSW</b> from the code segment to the code segment, executes in 1.39 µs per display memory access. That means that on average, 1.79 µs (more than 8 cycles!) are lost to the display adapter cycle-eater on each access. In other words, the display adapter cycle-eater can <i>more than double</i> the execution time of 8088 code!</p>
|
||||
|
||||
<p><b>LISTING 4.12 LST4-12.ASM</b></p><!-- CODE //-->
|
||||
<p><b>LISTING 4.12 LST4-12.ASM</b></p>
|
||||
<pre>
|
||||
; Times speed of memory access to normal system
|
||||
; memory.
|
||||
|
|
@ -105,7 +98,7 @@
|
|||
; is just to measure memory access
|
||||
; times
|
||||
call ZTimerOff
|
||||
</pre><!-- END CODE //-->
|
||||
</pre>
|
||||
|
||||
<p>Bear in mind that we’re talking about a worst case here; the impact of the display adapter cycle-eater is proportional to the percent of time a given code sequence spends accessing display memory.</p>
|
||||
|
||||
|
|
@ -126,10 +119,6 @@
|
|||
|
||||
<div align="center">
|
||||
Graphics Programming Black Book © 2001 Michael Abrash
|
||||
</div><!-- all of the reference materials (books) have the footer and subfoot reveresed -->
|
||||
<!-- reference_subfoot = footer -->
|
||||
<!-- reference_footer = subfoot -->
|
||||
<!-- BEGIN SUB FOOTER -->
|
||||
<!-- END FOOTER -->
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
21
04-10.html
21
04-10.html
|
|
@ -10,16 +10,9 @@
|
|||
<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: In the Lair of the Cycle-Eaters</title><!-- HEADER -->
|
||||
<!-- Empty Reference Subhead -->
|
||||
<!--ISBN=1576101746//-->
|
||||
<!--TITLE=Michael Abrash's Graphics Programming Black Book Special Edition//-->
|
||||
<!--AUTHOR=Michael Abrash//-->
|
||||
<!--PUBLISHER=The Coriolis Group, Inc.//-->
|
||||
<title>Michael Abrash's Graphics Programming Black Book Special Edition: In the Lair of the Cycle-Eaters</title>
|
||||
<meta name="chapter" content="04" />
|
||||
<meta name="pages" content="106-109" />
|
||||
<!--UNASSIGNED1//-->
|
||||
<!--UNASSIGNED2//-->
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
|
@ -49,7 +42,7 @@
|
|||
|
||||
<p>Nonetheless, the display adapter cycle-eater always takes its toll on graphics code. Interestingly, that toll becomes much higher on ATs and 80386 machines because while those computers can execute many more instructions per microsecond than can the 8088-based PC, it takes just as long to access display memory on those computers as on the 8088-based PC. Remember, the limited speed of access to a graphics adapter is an inherent characteristic of the adapter, so the fastest computer around can’t access display memory one iota faster than the adapter will allow.</p>
|
||||
|
||||
<h4 align="left"><a id="Heading22"></a>What to Do about the Display Adapter Cycle-Eater?</h4>
|
||||
<h4 align="left" id="Heading22">What to Do about the Display Adapter Cycle-Eater?</h4>
|
||||
|
||||
<p>What can we do about the display adapter cycle-eater? Well, we can minimize display memory accesses whenever possible. In particular, we can try to avoid read/modify/write display memory operations of the sort used to mask individual pixels and clip images. Why? Because read/modify/write operations require two display memory accesses (one read and one write) each time display memory is manipulated. Instead, we should try to use writes of the sort that set all the pixels in a given byte of display memory at once, since such writes don’t require accompanying read accesses. The key here is that only half as many display memory accesses are required to write a byte to display memory as are required to read a byte from display memory, mask part of it off and alter the rest, and write the byte back to display memory. Half as many display memory accesses means half as many display memory wait states.</p>
|
||||
|
||||
|
|
@ -67,7 +60,7 @@
|
|||
|
||||
<p>It would be handy to explore the display adapter cycle-eater issue in depth, with lots of example code and execution timings, but alas, I don’t have the space for that right now. For the time being, all you really need to know about the display adapter cycle-eater is that on the 8088 you can lose more than 8 cycles of execution time on each access to display memory. For intensive access to display memory, the loss really can be as high as 8cycles (and up to 50, 100, or even more on 486s and Pentiums paired with slow VGAs), while for average graphics code the loss is closer to 4 cycles; in either case, the impact on performance is significant. There is only one way to discover just how significant the impact of the display adapter cycle-eater is for any particular graphics code, and that is of course to measure the performance of that code.</p>
|
||||
|
||||
<h4 align="left"><a id="Heading23"></a>Cycle-Eaters: A Summary</h4>
|
||||
<h4 align="left" id="Heading23">Cycle-Eaters: A Summary</h4>
|
||||
|
||||
<p>We’ve covered a great deal of sophisticated material in this chapter, so don’t feel bad if you haven’t understood everything you’ve read; it will all become clear from further reading, especially once you study, time, and tune code that you have written yourself. What’s really important is that you come away from this chapter understanding that on the 8088:</p>
|
||||
|
||||
|
|
@ -83,7 +76,7 @@
|
|||
|
||||
<p>This basic knowledge about cycle-eaters puts you in a good position to understand the results reported by the Zen timer, and that means that you’re well on your way to writing high-performance assembler code.</p>
|
||||
|
||||
<h4 align="left"><a id="Heading24"></a>What Does It All Mean?</h4>
|
||||
<h4 align="left" id="Heading24">What Does It All Mean?</h4>
|
||||
|
||||
<p>There you have it: life under the programming interface. It’s not a particularly pretty picture for the inhabitants of that strange realm where hardware and software meet are little-known cycle-eaters that sap the speed from your unsuspecting code. Still, some of those cycle-eaters can be minimized by keeping instructions short, using the registers, using byte-sized memory operands, and accessing display memory as little as possible. None of the cycle-eaters can be eliminated, and dynamic RAM refresh can scarcely be addressed at all; still, aren’t you better off knowing how fast your code <i>really</i> runs—and why—than you were reading the official execution times and guessing? And while specific cycle-eaters vary in importance on later x86-family processors, with some cycle-eaters vanishing altogether and new ones appearing, the concept that understanding these obscure gremlins is a key to performance remains unchanged, as we’ll see again and again in later chapters.</p>
|
||||
|
||||
|
|
@ -104,10 +97,6 @@
|
|||
|
||||
<div align="center">
|
||||
Graphics Programming Black Book © 2001 Michael Abrash
|
||||
</div><!-- all of the reference materials (books) have the footer and subfoot reveresed -->
|
||||
<!-- reference_subfoot = footer -->
|
||||
<!-- reference_footer = subfoot -->
|
||||
<!-- BEGIN SUB FOOTER -->
|
||||
<!-- END FOOTER -->
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
23
05-01.html
23
05-01.html
|
|
@ -10,16 +10,9 @@
|
|||
<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: Crossing the Border</title><!-- HEADER -->
|
||||
<!-- Empty Reference Subhead -->
|
||||
<!--ISBN=1576101746//-->
|
||||
<!--TITLE=Michael Abrash's Graphics Programming Black Book Special Edition//-->
|
||||
<!--AUTHOR=Michael Abrash//-->
|
||||
<!--PUBLISHER=The Coriolis Group, Inc.//-->
|
||||
<title>Michael Abrash's Graphics Programming Black Book Special Edition: Crossing the Border</title>
|
||||
<meta name="chapter" content="05" />
|
||||
<meta name="pages" content="111-115" />
|
||||
<!--UNASSIGNED1//-->
|
||||
<!--UNASSIGNED2//-->
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
|
@ -37,10 +30,10 @@
|
|||
|
||||
<p><br /></p>
|
||||
|
||||
<h2><a id="Heading1"></a>Chapter 5<br />
|
||||
<h2 id="Heading1">Chapter 5<br />
|
||||
Crossing the Border</h2>
|
||||
|
||||
<h3><a id="Heading2"></a>Searching Files with Restartable Blocks</h3>
|
||||
<h3 id="Heading2">Searching Files with Restartable Blocks</h3>
|
||||
|
||||
<p><i>We just moved.</i> Those three little words should strike terror into the heart of anyone who owns more than a sleeping bag and a toothbrush. Our last move was the usual zoo—and then some. Because the distance from the old house to the new was only five miles, we used cars to move everything smaller than a washing machine. We have a sizable household—cats, dogs, kids, com, you name it—so the moving process took a number of car trips. A <i>large</i> number—33, to be exact. I personally spent about 15 hours just driving back and forth between the two houses. The move took days to complete.</p>
|
||||
|
||||
|
|
@ -66,7 +59,7 @@
|
|||
|
||||
<p>And with that, let’s look at a fairly complex application of restartable blocks.</p>
|
||||
|
||||
<h4 align="left"><a id="Heading3"></a>Searching for Text</h4>
|
||||
<h4 align="left" id="Heading3">Searching for Text</h4>
|
||||
|
||||
<p>The application we’re going to examine searches a file for a specified string. We’ll develop a program that will search the file specified on the command line for a string (also specified on the comline), then report whether the string was found or not. (Because the searched-for string is obtained via <b>argv</b>, it can’t contain any whitespace characters.)</p>
|
||||
|
||||
|
|
@ -78,7 +71,7 @@
|
|||
|
||||
<p>Well, it might be instructive to consider how we would search if our search involved only one buffer, already resident in memory. In other words, suppose we don’t have to bother with file handling at all, and further suppose that we don’t have to deal with searching through multiple blocks. After all, that’s a good description of the all-important inner loop of our searching program, where the program will spend virtually all of its time (aside from the unavoidable disk access overhead).</p>
|
||||
|
||||
<h3><a id="Heading4"></a>Avoiding the String Trap</h3>
|
||||
<h3 id="Heading4">Avoiding the String Trap</h3>
|
||||
|
||||
<p>The easiest approach would be to use a C/C<small>++</small> library function. The closest match to what we need is <b>strstr()</b>, which searches one string for the first occurrence of a second string. However, while <b>strstr()</b> would work, it isn’t ideal for our purposes. The problem is this: Where we want to search a fixed-length buffer for the first occurrence of a string, <b>strstr()</b> searches a <i>string</i> for the first occurrence of another string.</p>
|
||||
|
||||
|
|
@ -99,10 +92,6 @@
|
|||
|
||||
<div align="center">
|
||||
Graphics Programming Black Book © 2001 Michael Abrash
|
||||
</div><!-- all of the reference materials (books) have the footer and subfoot reveresed -->
|
||||
<!-- reference_subfoot = footer -->
|
||||
<!-- reference_footer = subfoot -->
|
||||
<!-- BEGIN SUB FOOTER -->
|
||||
<!-- END FOOTER -->
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
31
05-02.html
31
05-02.html
|
|
@ -10,16 +10,9 @@
|
|||
<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: Crossing the Border</title><!-- HEADER -->
|
||||
<!-- Empty Reference Subhead -->
|
||||
<!--ISBN=1576101746//-->
|
||||
<!--TITLE=Michael Abrash's Graphics Programming Black Book Special Edition//-->
|
||||
<!--AUTHOR=Michael Abrash//-->
|
||||
<!--PUBLISHER=The Coriolis Group, Inc.//-->
|
||||
<title>Michael Abrash's Graphics Programming Black Book Special Edition: Crossing the Border</title>
|
||||
<meta name="chapter" content="05" />
|
||||
<meta name="pages" content="115-118" />
|
||||
<!--UNASSIGNED1//-->
|
||||
<!--UNASSIGNED2//-->
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
|
@ -47,33 +40,31 @@
|
|||
</tr>
|
||||
</table>
|
||||
|
||||
<h3><a id="Heading5"></a>Brute-Force Techniques</h3>
|
||||
<h3 id="Heading5">Brute-Force Techniques</h3>
|
||||
|
||||
<p>Given that no C/C<small>++</small> library function meets our needs precisely, an obvious alternative approach is the brute-force technique that uses <b>memcmp()</b> to compare <i>every</i> potential matching location in the buffer to the string we’re searching for, as illustrated in Figure 5.1.</p>
|
||||
|
||||
<p>By the way, we could, of course, use our own code, working with pointers in a loop, to perform the comparison in place of <b>memcmp()</b>. But <b>memcmp()</b> will almost certainly use the very fast <b>REPZ CMPS</b> instruction. However, <i>never assume!</i> It wouldn’t hurt to use a debugger to check out the actual machine-code implementation of <b>memcmp()</b> from your compiler. If necessary, you could always write your own assembly language implementation of <b>memcmp()</b>.</p>
|
||||
|
||||
<p><a id="Fig1"><!-- </A><A HREF="javascript:displayWindow('images/05-01.jpg',415,211 )"> --><img src="images/05-01.jpg" /><br />
|
||||
<!-- </A>
|
||||
<BR><A HREF="javascript:displayWindow('images/05-01.jpg',415,211)"> --><b>Figure 5.1</b></a> <i>The brute-force searching technique.</i></p>
|
||||
<p><a id="Fig1"><img src="images/05-01.jpg" /><br />
|
||||
<b>Figure 5.1</b></a> <i>The brute-force searching technique.</i></p>
|
||||
|
||||
<p>Invoking <b>memcmp()</b> for each potential match location works, but entails considerable overhead. Each comparison requires that parameters be pushed and that a call to and return from <b>memcmp()</b> be performed, along with a pass through the comparison loop. Surely there’s a better way!</p>
|
||||
|
||||
<p>Indeed there is. We can eliminate most calls to <b>memcmp()</b> by performing a simple test on each potential match location that will reject most such locations right off the bat. We’ll just check whether the first character of the potentially matching buffer location matches the first character of the string we’re searching for. We could make this check by using a pointer in a loop to scan the buffer for the next match for the first character, stopping to check for a match with the rest of the string <i>only</i> when the first character matches, as shown in Figure 5.2.</p>
|
||||
|
||||
<h3><a id="Heading6"></a>Using memchr()</h3>
|
||||
<h3 id="Heading6">Using memchr()</h3>
|
||||
|
||||
<p>There’s yet a better way to implement this approach, however. Use the <b>memchr()</b> function, which does nothing more or less than find the next occurrence of a specified character in a fixed-length buffer (presumably by using the extremely efficient <b>REPNZ SCASB</b> instruction, although again it wouldn’t hurt to check). By using <b>memchr()</b> to scan for potential matches that can then be fully tested with <b>memcmp()</b>, we can build a highly efficient search engine that takes good advantage of the information we have about the buffer being searched and the string we’re searching for. Our engine also relies heavily on repeated string instructions, assuming that the <b>memchr()</b> and <b>memcmp()</b> library functions are properly coded.</p>
|
||||
|
||||
<p><a id="Fig2"><!-- </A><A HREF="javascript:displayWindow('images/05-02.jpg',409,241 )"> --><img src="images/05-02.jpg" /><br />
|
||||
<!-- </A>
|
||||
<BR><A HREF="javascript:displayWindow('images/05-02.jpg',409,241)"> --><b>Figure 5.2</b></a> <i>The faster string-searching technique.</i></p>
|
||||
<p><a id="Fig2"><img src="images/05-02.jpg" /><br />
|
||||
<b>Figure 5.2</b></a> <i>The faster string-searching technique.</i></p>
|
||||
|
||||
<p>We’re going to go with the this approach in our file-searching program; the only trick lies in deciding how to integrate this approach with restartable blocks in order to search through files larger than our buffer. This certainly isn’t the fastest-possible searching algorithm; as one example, the Boyer-Moore algorithm, which cleverly eliminates many buffer locations as potential matches in the process of checking preceding locations, can be considerably faster. However, the Boyer-Moore algorithm is quite complex to understand and implement, and would distract us from our main focus, restartable blocks, so we’ll save it for a later chapter (Chapter 14, to be precise). Besides, I suspect you’ll find the approach we’ll use to be fast enough for most purposes.</p>
|
||||
|
||||
<p>Now that we’ve selected a searching approach, let’s integrate it with file handling and searching through multiple blocks. In other words, let’s make it restartable.</p>
|
||||
|
||||
<h4 align="left"><a id="Heading7"></a>Making a Search Restartable</h4>
|
||||
<h4 align="left" id="Heading7">Making a Search Restartable</h4>
|
||||
|
||||
<p>As it happens, there’s no great trick to putting the pieces of this search program together. Basically, we’ll read in a buffer of data (we’ll work with 16K at a time to avoid signed overflow problems with integers), search it for a match with the <b>memchr()/memcmp()</b> engine described, and exit with a “string found” response if the desired string is found.</p>
|
||||
|
||||
|
|
@ -102,10 +93,6 @@
|
|||
|
||||
<div align="center">
|
||||
Graphics Programming Black Book © 2001 Michael Abrash
|
||||
</div><!-- all of the reference materials (books) have the footer and subfoot reveresed -->
|
||||
<!-- reference_subfoot = footer -->
|
||||
<!-- reference_footer = subfoot -->
|
||||
<!-- BEGIN SUB FOOTER -->
|
||||
<!-- END FOOTER -->
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
19
05-03.html
19
05-03.html
|
|
@ -10,16 +10,9 @@
|
|||
<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: Crossing the Border</title><!-- HEADER -->
|
||||
<!-- Empty Reference Subhead -->
|
||||
<!--ISBN=1576101746//-->
|
||||
<!--TITLE=Michael Abrash's Graphics Programming Black Book Special Edition//-->
|
||||
<!--AUTHOR=Michael Abrash//-->
|
||||
<!--PUBLISHER=The Coriolis Group, Inc.//-->
|
||||
<title>Michael Abrash's Graphics Programming Black Book Special Edition: Crossing the Border</title>
|
||||
<meta name="chapter" content="05" />
|
||||
<meta name="pages" content="118-121" />
|
||||
<!--UNASSIGNED1//-->
|
||||
<!--UNASSIGNED2//-->
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
|
@ -37,7 +30,7 @@
|
|||
|
||||
<p><br /></p>
|
||||
|
||||
<p><b>LISTING 5.1 SEARCH.C</b></p><!-- CODE //-->
|
||||
<p><b>LISTING 5.1 SEARCH.C</b></p>
|
||||
<pre>
|
||||
/* Program to search the file specified by the first command-line
|
||||
* argument for the string specified by the second command-line
|
||||
|
|
@ -204,7 +197,7 @@ main(int argc, char *argv[]) {
|
|||
exit(Found); /* Return the found/not found status as the
|
||||
DOS errorlevel */
|
||||
}
|
||||
</pre><!-- END CODE //-->
|
||||
</pre>
|
||||
|
||||
<p><br /></p>
|
||||
|
||||
|
|
@ -223,10 +216,6 @@ main(int argc, char *argv[]) {
|
|||
|
||||
<div align="center">
|
||||
Graphics Programming Black Book © 2001 Michael Abrash
|
||||
</div><!-- all of the reference materials (books) have the footer and subfoot reveresed -->
|
||||
<!-- reference_subfoot = footer -->
|
||||
<!-- reference_footer = subfoot -->
|
||||
<!-- BEGIN SUB FOOTER -->
|
||||
<!-- END FOOTER -->
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
19
05-04.html
19
05-04.html
|
|
@ -10,16 +10,9 @@
|
|||
<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: Crossing the Border</title><!-- HEADER -->
|
||||
<!-- Empty Reference Subhead -->
|
||||
<!--ISBN=1576101746//-->
|
||||
<!--TITLE=Michael Abrash's Graphics Programming Black Book Special Edition//-->
|
||||
<!--AUTHOR=Michael Abrash//-->
|
||||
<!--PUBLISHER=The Coriolis Group, Inc.//-->
|
||||
<title>Michael Abrash's Graphics Programming Black Book Special Edition: Crossing the Border</title>
|
||||
<meta name="chapter" content="05" />
|
||||
<meta name="pages" content="121-122" />
|
||||
<!--UNASSIGNED1//-->
|
||||
<!--UNASSIGNED2//-->
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
|
@ -37,7 +30,7 @@
|
|||
|
||||
<p><br /></p>
|
||||
|
||||
<h3><a id="Heading8"></a>Interpreting Where the Cycles Go</h3>
|
||||
<h3 id="Heading8">Interpreting Where the Cycles Go</h3>
|
||||
|
||||
<p>To boost the overall performance of Listing 5.1, I would normally convert <b>SearchForString()</b> to assembly language at this point. However, I’m not going to do that, and the reason is as important a lesson as any discussion of optimized assembly code is likely to be. Take a moment to examine some interesting performance aspects of the C implementation, and all should become much clearer.</p>
|
||||
|
||||
|
|
@ -53,7 +46,7 @@
|
|||
|
||||
<p>Not likely.</p>
|
||||
|
||||
<h4 align="left"><a id="Heading9"></a>Knowing When Assembly Is Pointless</h4>
|
||||
<h4 align="left" id="Heading9">Knowing When Assembly Is Pointless</h4>
|
||||
|
||||
<p>So that’s why we’re not going to go to assembly language in this example—which is not to say it would never be worth converting the search engine in Listing 5.1 to assembly.</p>
|
||||
|
||||
|
|
@ -78,10 +71,6 @@
|
|||
|
||||
<div align="center">
|
||||
Graphics Programming Black Book © 2001 Michael Abrash
|
||||
</div><!-- all of the reference materials (books) have the footer and subfoot reveresed -->
|
||||
<!-- reference_subfoot = footer -->
|
||||
<!-- reference_footer = subfoot -->
|
||||
<!-- BEGIN SUB FOOTER -->
|
||||
<!-- END FOOTER -->
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
17
05-05.html
17
05-05.html
|
|
@ -10,16 +10,9 @@
|
|||
<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: Crossing the Border</title><!-- HEADER -->
|
||||
<!-- Empty Reference Subhead -->
|
||||
<!--ISBN=1576101746//-->
|
||||
<!--TITLE=Michael Abrash's Graphics Programming Black Book Special Edition//-->
|
||||
<!--AUTHOR=Michael Abrash//-->
|
||||
<!--PUBLISHER=The Coriolis Group, Inc.//-->
|
||||
<title>Michael Abrash's Graphics Programming Black Book Special Edition: Crossing the Border</title>
|
||||
<meta name="chapter" content="05" />
|
||||
<meta name="pages" content="122-123" />
|
||||
<!--UNASSIGNED1//-->
|
||||
<!--UNASSIGNED2//-->
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
|
@ -53,7 +46,7 @@
|
|||
|
||||
<p>Restartable blocks do minimize the overhead of DOS file-access calls in Listing 5.1; it’s just that there’s no way to reduce that overhead to the point where it becomes worth attempting to further improve the performance of our relatively efficient search engine. Although the search engine is by no means fully optimized, it’s nonetheless as fast as there’s any reason for it to be, given the balance of performance among the components of this program.</p>
|
||||
|
||||
<h3><a id="Heading10"></a>Always Look Where Execution Is Going</h3>
|
||||
<h3 id="Heading10">Always Look Where Execution Is Going</h3>
|
||||
|
||||
<p>I’ve explained two important lessons: Know when it’s worth optimizing further, and use restartable blocks to process large data sets as a series of blocks, with each block handled at high speed. The first lesson is less obvious than it seems.</p>
|
||||
|
||||
|
|
@ -90,10 +83,6 @@
|
|||
|
||||
<div align="center">
|
||||
Graphics Programming Black Book © 2001 Michael Abrash
|
||||
</div><!-- all of the reference materials (books) have the footer and subfoot reveresed -->
|
||||
<!-- reference_subfoot = footer -->
|
||||
<!-- reference_footer = subfoot -->
|
||||
<!-- BEGIN SUB FOOTER -->
|
||||
<!-- END FOOTER -->
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
29
06-01.html
29
06-01.html
|
|
@ -10,16 +10,9 @@
|
|||
<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: Looking Past Face Value</title><!-- HEADER -->
|
||||
<!-- Empty Reference Subhead -->
|
||||
<!--ISBN=1576101746//-->
|
||||
<!--TITLE=Michael Abrash's Graphics Programming Black Book Special Edition//-->
|
||||
<!--AUTHOR=Michael Abrash//-->
|
||||
<!--PUBLISHER=The Coriolis Group, Inc.//-->
|
||||
<title>Michael Abrash's Graphics Programming Black Book Special Edition: Looking Past Face Value</title>
|
||||
<meta name="chapter" content="06" />
|
||||
<meta name="pages" content="125-129" />
|
||||
<!--UNASSIGNED1//-->
|
||||
<!--UNASSIGNED2//-->
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
|
@ -37,10 +30,10 @@
|
|||
|
||||
<p><br /></p>
|
||||
|
||||
<h2><a id="Heading1"></a>Chapter 6<br />
|
||||
<h2 id="Heading1">Chapter 6<br />
|
||||
Looking Past Face Value</h2>
|
||||
|
||||
<h3><a id="Heading2"></a>How Machine Instructions May Do More Than You Think</h3>
|
||||
<h3 id="Heading2">How Machine Instructions May Do More Than You Think</h3>
|
||||
|
||||
<p>I first met Jeff Duntemann at an authors’ dinner hosted by <i>PC Tech Journal</i> at Fall Comdex, back in 1985. Jeff was already reasonably well-known as a computer editor and writer, although not as famous as <i>Complete Turbo Pascal</i>, editions 1 through 672 (or thereabouts), <i>TURBO TECHNIX</i>, and <i>PC TECHNIQUES</i> would soon make him. I was fortunate enough to be seated next to Jeff at the dinner table, and, not surprisingly, our often animated conversation revolved around computers, computer writing, and more computers (not necessarily in that order).</p>
|
||||
|
||||
|
|
@ -66,7 +59,7 @@
|
|||
|
||||
<p>In short, the x86 family can do much more than you think—if you’ll use everything it has to offer. Give it a shot!</p>
|
||||
|
||||
<h4 align="left"><a id="Heading3"></a>Memory Addressing and Arithmetic</h4>
|
||||
<h4 align="left" id="Heading3">Memory Addressing and Arithmetic</h4>
|
||||
|
||||
<p>Years ago, I saw a clip on the David Letterman show in which Letterman walked into a store by the name of “Just Lamps” and asked, “So what do you sell here?”</p>
|
||||
|
||||
|
|
@ -82,16 +75,16 @@
|
|||
|
||||
<p>They perform arithmetic, that’s what they do, and that’s a distinctly different and often useful perspective on memory address calculations.</p>
|
||||
|
||||
<p>For example, suppose you have an array base address in BX and an index into the array in SI. You could add the two registers together to address memory, like this:</p><!-- CODE SNIP //-->
|
||||
<p>For example, suppose you have an array base address in BX and an index into the array in SI. You could add the two registers together to address memory, like this:</p>
|
||||
<pre>
|
||||
add bx,si
|
||||
mov al,[bx]
|
||||
</pre><!-- END CODE SNIP //-->
|
||||
</pre>
|
||||
|
||||
<p>Or you could let the processor do the arithmetic for you in a single instruction:</p><!-- CODE SNIP //-->
|
||||
<p>Or you could let the processor do the arithmetic for you in a single instruction:</p>
|
||||
<pre>
|
||||
mov al,[bx+si]
|
||||
</pre><!-- END CODE SNIP //-->
|
||||
</pre>
|
||||
|
||||
<p><br /></p>
|
||||
|
||||
|
|
@ -110,10 +103,6 @@ mov al,[bx+si]
|
|||
|
||||
<div align="center">
|
||||
Graphics Programming Black Book © 2001 Michael Abrash
|
||||
</div><!-- all of the reference materials (books) have the footer and subfoot reveresed -->
|
||||
<!-- reference_subfoot = footer -->
|
||||
<!-- reference_footer = subfoot -->
|
||||
<!-- BEGIN SUB FOOTER -->
|
||||
<!-- END FOOTER -->
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
67
06-02.html
67
06-02.html
|
|
@ -10,16 +10,9 @@
|
|||
<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: Looking Past Face Value</title><!-- HEADER -->
|
||||
<!-- Empty Reference Subhead -->
|
||||
<!--ISBN=1576101746//-->
|
||||
<!--TITLE=Michael Abrash's Graphics Programming Black Book Special Edition//-->
|
||||
<!--AUTHOR=Michael Abrash//-->
|
||||
<!--PUBLISHER=The Coriolis Group, Inc.//-->
|
||||
<title>Michael Abrash's Graphics Programming Black Book Special Edition: Looking Past Face Value</title>
|
||||
<meta name="chapter" content="06" />
|
||||
<meta name="pages" content="129-133" />
|
||||
<!--UNASSIGNED1//-->
|
||||
<!--UNASSIGNED2//-->
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
|
@ -37,14 +30,14 @@
|
|||
|
||||
<p><br /></p>
|
||||
|
||||
<p>The two approaches are functionally interchangeable but <i>not</i> equivalent from a performance standpoint, and which is better depends on the particular context. If it’s a one-shot memory access, it’s best to let the processor perform the addition; it’s generally faster at doing this than a separate <b>ADD</b> instruction would be. If it’s a memory access within a loop, however, it’s advantageous on the 8088 CPU to perform the addition outside the loop, if possible, reducing effective address calculation time inside the loop, as in the following:</p><!-- CODE SNIP //-->
|
||||
<p>The two approaches are functionally interchangeable but <i>not</i> equivalent from a performance standpoint, and which is better depends on the particular context. If it’s a one-shot memory access, it’s best to let the processor perform the addition; it’s generally faster at doing this than a separate <b>ADD</b> instruction would be. If it’s a memory access within a loop, however, it’s advantageous on the 8088 CPU to perform the addition outside the loop, if possible, reducing effective address calculation time inside the loop, as in the following:</p>
|
||||
<pre>
|
||||
add bx,si
|
||||
LoopTop:
|
||||
mov al,[bx]
|
||||
inc bx
|
||||
loop LoopTop
|
||||
</pre><!-- END CODE SNIP //-->
|
||||
</pre>
|
||||
|
||||
<p>Here, <b>MOV AL,[BX]</b> is two cycles faster than <b>MOV AL,[BX+SI]</b>.</p>
|
||||
|
||||
|
|
@ -52,7 +45,7 @@ LoopTop:
|
|||
|
||||
<p>The 486 is an odd case, in which the use of an index register or the use of a base register that’s the destination of the previous instruction may slow things down, so it is generally but not always better to perform the addition outside the loop on the 486. All memory addressing calculations are free on the Pentium, however. I’ll discuss 486 performance issues in Chapters 12 and 13, and the Pentium in Chapters 19 through 21.</p>
|
||||
|
||||
<h3><a id="Heading4"></a>Math via Memory Addressing</h3>
|
||||
<h3 id="Heading4">Math via Memory Addressing</h3>
|
||||
|
||||
<p>You’re probably not particularly wowed to hear that you can use addressing modes to perform memory addressing arithmetic that would otherwise have to be performed with separate arithmetic instructions. You may, however, be a tad more interested to hear that you can also use addressing modes to perform arithmetic that has nothing to do with memory addressing, and with a couple of advantages over arithmetic instructions, at that.</p>
|
||||
|
||||
|
|
@ -62,75 +55,73 @@ LoopTop:
|
|||
|
||||
<p>What does that give us? Two things that <b>ADD</b> doesn’t provide: the ability to perform addition with either two or three operands, and the ability to store the result in <i>any</i> register, not just in one of the source operands.</p>
|
||||
|
||||
<p>Imagine that we want to add BX to DI, add two to the result, and store the result in AX. The obvious solution is this:</p><!-- CODE SNIP //-->
|
||||
<p>Imagine that we want to add BX to DI, add two to the result, and store the result in AX. The obvious solution is this:</p>
|
||||
<pre>
|
||||
mov ax,bx
|
||||
add ax,di
|
||||
add ax,2
|
||||
</pre><!-- END CODE SNIP //-->
|
||||
</pre>
|
||||
|
||||
<p>(It would be more compact to increment AX twice than to add two to it, and would probably be faster on an 8088, but that’s not what we’re after at the moment.) An elegant alternative solution is simply:</p><!-- CODE SNIP //-->
|
||||
<p>(It would be more compact to increment AX twice than to add two to it, and would probably be faster on an 8088, but that’s not what we’re after at the moment.) An elegant alternative solution is simply:</p>
|
||||
<pre>
|
||||
lea ax,[bx+di+2]
|
||||
</pre><!-- END CODE SNIP //-->
|
||||
</pre>
|
||||
|
||||
<p>Likewise, either of the following would copy SI plus two to DI</p><!-- CODE SNIP //-->
|
||||
<p>Likewise, either of the following would copy SI plus two to DI</p>
|
||||
<pre>
|
||||
mov di,si
|
||||
add di,2
|
||||
</pre><!-- END CODE SNIP //-->
|
||||
</pre>
|
||||
|
||||
<p>or:</p><!-- CODE SNIP //-->
|
||||
<p>or:</p>
|
||||
<pre>
|
||||
lea di,[si+2]
|
||||
</pre><!-- END CODE SNIP //-->
|
||||
</pre>
|
||||
|
||||
<p>Mind you, the only components <b>LEA</b> can add are BX or BP, SI or DI, and a constant displacement, so it’s not going to replace <b>ADD</b> most of the time. Also, <b>LEA</b> is considerably slower than <b>ADD</b> on an 8088, although it is just as fast as <b>ADD</b> on a 286 or 386 when fewer than three memory addressing components are used. <b>LEA</b> is 1 cycle slower than <b>ADD</b> on a 486 if the sum of two registers is used to point to memory, but no slower than <b>ADD</b> on a Pentium. On both a 486 and Pentium, <b>LEA</b> can also be slowed down by addressing interlocks.</p>
|
||||
|
||||
<p><a id="Fig1"><!-- </A><A HREF="javascript:displayWindow('images/06-01.jpg',418,276 )"> --><img src="images/06-01.jpg" /><br />
|
||||
<!-- </A>
|
||||
<BR><A HREF="javascript:displayWindow('images/06-01.jpg',418,276)"> --><b>Figure 6.1</b></a> <i>Operation of ADD Reg,Reg vs. LEA Reg,{Addr}.</i></p>
|
||||
<p><a id="Fig1"><img src="images/06-01.jpg" /><br />
|
||||
<b>Figure 6.1</b></a> <i>Operation of ADD Reg,Reg vs. LEA Reg,{Addr}.</i></p>
|
||||
|
||||
<h4 align="left"><a id="Heading5"></a>The Wonders of LEA on the 386</h4>
|
||||
<h4 align="left" id="Heading5">The Wonders of LEA on the 386</h4>
|
||||
|
||||
<p><b>LEA</b> really comes into its own as a “super-ADD” instruction on the 386, 486, and Pentium, where it can take advantage of the enhanced memory addressing modes of those processors. (The 486 and Pentium offer the same modes as the 386, so I’ll refer only to the 386 from now on.) The 386 can do two very interesting things: It can use <i>any</i> 32-bit register (EAX, EBX, and so on) as the memory addressing base register and/or the memory addressing index register, and it can multiply any 32-bit register used as an index by two, four, or eight in the process of calculating a memory address, as shown in Figure 6.2. Let’s see what that’s good for.</p>
|
||||
|
||||
<p>Well, the obvious advantage is that any two 32-bit registers, or any 32-bit register and any constant, or any two 32-bit registers and any constant, can be added together, with the result stored in any register. This makes the 32-bit <b>LEA</b> much more generally useful than the standard 16-bit <b>LEA</b> in the role of an <b>ADD</b> with an independent destination.</p>
|
||||
|
||||
<p><a id="Fig2"><!-- </A><A HREF="javascript:displayWindow('images/06-02.jpg',421,197 )"> --><img src="images/06-02.jpg" /><br />
|
||||
<!-- </A>
|
||||
<BR><A HREF="javascript:displayWindow('images/06-02.jpg',421,197)"> --><b>Figure 6.2</b></a> <i>Operation of the 32-bit LEA reg,[Addr].</i></p>
|
||||
<p><a id="Fig2"><img src="images/06-02.jpg" /><br />
|
||||
<b>Figure 6.2</b></a> <i>Operation of the 32-bit LEA reg,[Addr].</i></p>
|
||||
|
||||
<p>But what else can <b>LEA</b> do on a 386, besides add?</p>
|
||||
|
||||
<p>It can multiply any register used as an index. <b>LEA</b> can multiply only by the power-of-two values 2, 4, or 8, but that’s useful more often than you might imagine, especially when dealing with pointers into tables. Besides, multiplying by 2, 4, or 8 amounts to a left shift of 1, 2, or 3 bits, so we can now add up to two 32-bit registers and a constant, <i>and</i> shift (or multiply) one of the registers to some extent—all with a single instruction. For example,</p><!-- CODE SNIP //-->
|
||||
<p>It can multiply any register used as an index. <b>LEA</b> can multiply only by the power-of-two values 2, 4, or 8, but that’s useful more often than you might imagine, especially when dealing with pointers into tables. Besides, multiplying by 2, 4, or 8 amounts to a left shift of 1, 2, or 3 bits, so we can now add up to two 32-bit registers and a constant, <i>and</i> shift (or multiply) one of the registers to some extent—all with a single instruction. For example,</p>
|
||||
<pre>
|
||||
lea edi,TableBase[ecx+edx*4]
|
||||
</pre><!-- END CODE SNIP //-->
|
||||
</pre>
|
||||
|
||||
<p>replaces all this</p><!-- CODE SNIP //-->
|
||||
<p>replaces all this</p>
|
||||
<pre>
|
||||
mov edi,edx
|
||||
shl edi,2
|
||||
add edi,ecx
|
||||
add edi,offset TableBase
|
||||
</pre><!-- END CODE SNIP //-->
|
||||
</pre>
|
||||
|
||||
<p>when pointing to an entry in a doubly indexed table.</p>
|
||||
|
||||
<h3><a id="Heading6"></a>Multiplication with LEA Using Non-Powers of Two</h3>
|
||||
<h3 id="Heading6">Multiplication with LEA Using Non-Powers of Two</h3>
|
||||
|
||||
<p>Are you impressed yet with all that <b>LEA</b> can do on the 386? Believe it or not, one more feature still awaits us. <b>LEA</b> can actually perform a fast multiply of a 32-bit register by some values <i>other</i> than powers of two. You see, the same 32-bit register can be both base and index on the 386, and can be scaled as the index while being used unchanged as the base. That means that you can, for example, multiply EBX by 5 with:</p><!-- CODE SNIP //-->
|
||||
<p>Are you impressed yet with all that <b>LEA</b> can do on the 386? Believe it or not, one more feature still awaits us. <b>LEA</b> can actually perform a fast multiply of a 32-bit register by some values <i>other</i> than powers of two. You see, the same 32-bit register can be both base and index on the 386, and can be scaled as the index while being used unchanged as the base. That means that you can, for example, multiply EBX by 5 with:</p>
|
||||
<pre>
|
||||
lea ebx,[ebx+ebx*4]
|
||||
</pre><!-- END CODE SNIP //-->
|
||||
</pre>
|
||||
|
||||
<p>Without <b>LEA</b> and scaling, multiplication of EBX by 5 would require either a relatively slow <b>MUL</b>, along with a set-up instruction or two, or three separate instructions along the lines of the following</p><!-- CODE SNIP //-->
|
||||
<p>Without <b>LEA</b> and scaling, multiplication of EBX by 5 would require either a relatively slow <b>MUL</b>, along with a set-up instruction or two, or three separate instructions along the lines of the following</p>
|
||||
<pre>
|
||||
mov edx,ebx
|
||||
shl ebx,2
|
||||
add ebx,edx
|
||||
</pre><!-- END CODE SNIP //-->
|
||||
</pre>
|
||||
|
||||
<p>and would in either case require the destruction of the contents of another register.</p>
|
||||
|
||||
|
|
@ -163,10 +154,6 @@ add ebx,edx
|
|||
|
||||
<div align="center">
|
||||
Graphics Programming Black Book © 2001 Michael Abrash
|
||||
</div><!-- all of the reference materials (books) have the footer and subfoot reveresed -->
|
||||
<!-- reference_subfoot = footer -->
|
||||
<!-- reference_footer = subfoot -->
|
||||
<!-- BEGIN SUB FOOTER -->
|
||||
<!-- END FOOTER -->
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
21
07-01.html
21
07-01.html
|
|
@ -10,16 +10,9 @@
|
|||
<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: Local Optimization</title><!-- HEADER -->
|
||||
<!-- Empty Reference Subhead -->
|
||||
<!--ISBN=1576101746//-->
|
||||
<!--TITLE=Michael Abrash's Graphics Programming Black Book Special Edition//-->
|
||||
<!--AUTHOR=Michael Abrash//-->
|
||||
<!--PUBLISHER=The Coriolis Group, Inc.//-->
|
||||
<title>Michael Abrash's Graphics Programming Black Book Special Edition: Local Optimization</title>
|
||||
<meta name="chapter" content="07" />
|
||||
<meta name="pages" content="136-139" />
|
||||
<!--UNASSIGNED1//-->
|
||||
<!--UNASSIGNED2//-->
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
|
@ -37,10 +30,10 @@
|
|||
|
||||
<p><br /></p>
|
||||
|
||||
<h2><a id="Heading1"></a>Chapter 7<br />
|
||||
<h2 id="Heading1">Chapter 7<br />
|
||||
Local Optimization</h2>
|
||||
|
||||
<h3><a id="Heading2"></a>Optimizing Halfway between Algorithms and Cycle Counting</h3>
|
||||
<h3 id="Heading2">Optimizing Halfway between Algorithms and Cycle Counting</h3>
|
||||
|
||||
<p>You might not think it, but there’s much to learn about performance programming from the Great Buffalo Sauna Fiasco. To wit:</p>
|
||||
|
||||
|
|
@ -74,7 +67,7 @@
|
|||
|
||||
<p>And yes, in case you’re wondering, the above story is indeed true. Was I there? Let me put it this way: If I were, I’d never admit it!</p>
|
||||
|
||||
<h4 align="left"><a id="Heading3"></a>When LOOP Is a Bad Idea</h4>
|
||||
<h4 align="left" id="Heading3">When LOOP Is a Bad Idea</h4>
|
||||
|
||||
<p>Let’s examine first an instruction that is less than it appears to be: <b>LOOP</b>. There’s no mystery about what <b>LOOP</b> does; it decrements CX and branches if CX doesn’t decrement to zero. It’s so beautifully suited to the task of counting down loops that any experienced x86 programmer instinctively stuffs the loop count in CX and reaches for <b>LOOP</b> when setting up a loop. That’s fine—<b>LOOP</b> does, of course, work as advertised—but there is one problem:</p>
|
||||
|
||||
|
|
@ -113,10 +106,6 @@
|
|||
|
||||
<div align="center">
|
||||
Graphics Programming Black Book © 2001 Michael Abrash
|
||||
</div><!-- all of the reference materials (books) have the footer and subfoot reveresed -->
|
||||
<!-- reference_subfoot = footer -->
|
||||
<!-- reference_footer = subfoot -->
|
||||
<!-- BEGIN SUB FOOTER -->
|
||||
<!-- END FOOTER -->
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
29
07-02.html
29
07-02.html
|
|
@ -10,16 +10,9 @@
|
|||
<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: Local Optimization</title><!-- HEADER -->
|
||||
<!-- Empty Reference Subhead -->
|
||||
<!--ISBN=1576101746//-->
|
||||
<!--TITLE=Michael Abrash's Graphics Programming Black Book Special Edition//-->
|
||||
<!--AUTHOR=Michael Abrash//-->
|
||||
<!--PUBLISHER=The Coriolis Group, Inc.//-->
|
||||
<title>Michael Abrash's Graphics Programming Black Book Special Edition: Local Optimization</title>
|
||||
<meta name="chapter" content="07" />
|
||||
<meta name="pages" content="139-141" />
|
||||
<!--UNASSIGNED1//-->
|
||||
<!--UNASSIGNED2//-->
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
|
@ -37,31 +30,31 @@
|
|||
|
||||
<p><br /></p>
|
||||
|
||||
<p>By the way, don’t fall victim to the lures of <b>JCXZ</b> and do something like this:</p><!-- CODE SNIP //-->
|
||||
<p>By the way, don’t fall victim to the lures of <b>JCXZ</b> and do something like this:</p>
|
||||
<pre>
|
||||
and cx,ofh ;Isolate the desired field
|
||||
jcxz SkipLoop ;If field is 0, don’t bother
|
||||
</pre><!-- END CODE SNIP //-->
|
||||
</pre>
|
||||
|
||||
<p>The <b>AND</b> instruction has already set the Zero flag, so this</p><!-- CODE SNIP //-->
|
||||
<p>The <b>AND</b> instruction has already set the Zero flag, so this</p>
|
||||
<pre>
|
||||
and cx,0fh ;Isolate the desired field
|
||||
jz SkipLoop ;If field is 0, don’t bother
|
||||
</pre><!-- END CODE SNIP //-->
|
||||
</pre>
|
||||
|
||||
<p>will do just fine and is faster on all processors. Use <b>JCXZ</b> only when the Zero flag isn’t already set to reflect the status of CX.</p>
|
||||
|
||||
<h3><a id="Heading4"></a>The Lessons of LOOP and JCXZ</h3>
|
||||
<h3 id="Heading4">The Lessons of LOOP and JCXZ</h3>
|
||||
|
||||
<p>What can we learn from <b>LOOP</b> and <b>JCXZ?</b> First, that a single instruction that is intended to do a complex task is not necessarily faster than several instructions that together do the same thing. Second, that the relative merits of instructions and optimization rules vary to a surprisingly large degree across the x86 family.</p>
|
||||
|
||||
<p>In particular, if you’re going to write 386 protected mode code, which will run only on the 386, 486, and Pentium, you’d be well advised to rethink your use of the more esoteric members of the x86 instruction set. <b>LOOP, JCXZ,</b> the various accumulator-specific instructions, and even the string instructions in many circumstances no longer offer the advantages they did on the 8088. Sometimes they’re just not any faster than more general instructions, so they’re not worth going out of your way to use; sometimes, as with <b>LOOP,</b> they’re actually slower, and you’d do well to avoid them altogether in the 386/486 world. Reviewing the instruction cycle times in the MASM or TASM manuals, or looking over the cycle times in Intel’s literature, is a good place to start; published cycle times are closer to actual execution times on the 386 and 486 than on the 8088, and are reasonably reliable indicators of the relative performance levels of x86 instructions.</p>
|
||||
|
||||
<h4 align="left"><a id="Heading5"></a>Avoiding LOOPS of Any Stripe</h4>
|
||||
<h4 align="left" id="Heading5">Avoiding LOOPS of Any Stripe</h4>
|
||||
|
||||
<p>Cycle counting and directly substituting instructions (<b>DEC CX/JNZ</b> for <b>LOOP,</b> for example) are techniques that belong at the lowest level of optimization. It’s an important level, but it’s fairly mechanical; once you’ve learned the capabilities and relative performance levels of the various instructions, you should be able to select the best instructions fairly easily. What’s more, this is a task at which compilers excel. What I’m saying is that you shouldn’t get too caught up in counting cycles because that’s a small (albeit important) part of the optimization picture, and not the area in which your greatest advantage lies.</p>
|
||||
|
||||
<h3><a id="Heading6"></a>Local Optimization</h3>
|
||||
<h3 id="Heading6">Local Optimization</h3>
|
||||
|
||||
<p>One level at which assembly language programming pays off handsomely is that of <i>local optimization;</i> that is, selecting the best <i>sequence</i> of instructions for a task. The key to local optimization is viewing the 80x86 instruction set as a set of building blocks, each with unique characteristics. Your job is to sequence those blocks so that they perform well. It doesn’t matter what the instructions are intended to do or what their names are; all that matters is what they <i>do.</i></p>
|
||||
|
||||
|
|
@ -98,10 +91,6 @@ jz SkipLoop ;If field is 0, don’t bother
|
|||
|
||||
<div align="center">
|
||||
Graphics Programming Black Book © 2001 Michael Abrash
|
||||
</div><!-- all of the reference materials (books) have the footer and subfoot reveresed -->
|
||||
<!-- reference_subfoot = footer -->
|
||||
<!-- reference_footer = subfoot -->
|
||||
<!-- BEGIN SUB FOOTER -->
|
||||
<!-- END FOOTER -->
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
21
07-03.html
21
07-03.html
|
|
@ -10,16 +10,9 @@
|
|||
<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: Local Optimization</title><!-- HEADER -->
|
||||
<!-- Empty Reference Subhead -->
|
||||
<!--ISBN=1576101746//-->
|
||||
<!--TITLE=Michael Abrash's Graphics Programming Black Book Special Edition//-->
|
||||
<!--AUTHOR=Michael Abrash//-->
|
||||
<!--PUBLISHER=The Coriolis Group, Inc.//-->
|
||||
<title>Michael Abrash's Graphics Programming Black Book Special Edition: Local Optimization</title>
|
||||
<meta name="chapter" content="07" />
|
||||
<meta name="pages" content="141-143" />
|
||||
<!--UNASSIGNED1//-->
|
||||
<!--UNASSIGNED2//-->
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
|
@ -37,7 +30,7 @@
|
|||
|
||||
<p><br /></p>
|
||||
|
||||
<p><b>LISTING 7.1 L7-1.ASM</b></p><!-- CODE //-->
|
||||
<p><b>LISTING 7.1 L7-1.ASM</b></p>
|
||||
<pre>
|
||||
; Program to illustrate searching through a buffer of a specified
|
||||
; length until either a specified byte or a zero byte is
|
||||
|
|
@ -129,9 +122,9 @@ ByteFound:
|
|||
ret
|
||||
SearchMaxLengthendp
|
||||
end Start
|
||||
</pre><!-- END CODE //-->
|
||||
</pre>
|
||||
|
||||
<h3><a id="Heading7"></a>Unrolling Loops</h3>
|
||||
<h3 id="Heading7">Unrolling Loops</h3>
|
||||
|
||||
<p>Listing 7.2 takes a different tack, unrolling the loop so that four bytes are checked for each <b>LOOP</b> performed. The same instructions are used inside the loop in each listing, but Listing 7.2 is arranged so that three-quarters of the <b>LOOP</b>s are eliminated. Listings 7.1 and 7.2 perform exactly the same task, and they use the same instructions in the loop—the searching algorithm hasn’t changed in any way—but we have sequenced the instructions differently in Listing 7.2, and that makes all the difference.</p>
|
||||
|
||||
|
|
@ -152,10 +145,6 @@ SearchMaxLengthendp
|
|||
|
||||
<div align="center">
|
||||
Graphics Programming Black Book © 2001 Michael Abrash
|
||||
</div><!-- all of the reference materials (books) have the footer and subfoot reveresed -->
|
||||
<!-- reference_subfoot = footer -->
|
||||
<!-- reference_footer = subfoot -->
|
||||
<!-- BEGIN SUB FOOTER -->
|
||||
<!-- END FOOTER -->
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
19
07-04.html
19
07-04.html
|
|
@ -10,16 +10,9 @@
|
|||
<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: Local Optimization</title><!-- HEADER -->
|
||||
<!-- Empty Reference Subhead -->
|
||||
<!--ISBN=1576101746//-->
|
||||
<!--TITLE=Michael Abrash's Graphics Programming Black Book Special Edition//-->
|
||||
<!--AUTHOR=Michael Abrash//-->
|
||||
<!--PUBLISHER=The Coriolis Group, Inc.//-->
|
||||
<title>Michael Abrash's Graphics Programming Black Book Special Edition: Local Optimization</title>
|
||||
<meta name="chapter" content="07" />
|
||||
<meta name="pages" content="143-145" />
|
||||
<!--UNASSIGNED1//-->
|
||||
<!--UNASSIGNED2//-->
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
|
@ -37,7 +30,7 @@
|
|||
|
||||
<p><br /></p>
|
||||
|
||||
<p><b>LISTING 7.2 L7-2.ASM</b></p><!-- CODE //-->
|
||||
<p><b>LISTING 7.2 L7-2.ASM</b></p>
|
||||
<pre>
|
||||
; Program to illustrate searching through a buffer of a specified
|
||||
; length until a specified zero byte is encountered.
|
||||
|
|
@ -167,7 +160,7 @@ ByteFound:
|
|||
ret
|
||||
SearchMaxLengthendp
|
||||
end Start
|
||||
</pre><!-- END CODE //-->
|
||||
</pre>
|
||||
|
||||
<p>How much difference? Listing 7.2 runs in 121 µs—40 percent faster than Listing 7.1, even though Listing 7.2 still uses <b>LOOP</b> rather than <b>DEC CX/JNZ.</b> (The loop in Listing 7.2 could be unrolled further, too; it’s just a question of how much more memory you want to trade for ever-decreasing performance benefits.) That’s typical of local optimization; it won’t often yield the order-of-magnitude improvements that algorithmic improvements can produce, but it can get you a critical 50 percent or 100 percent improvement when you’ve exhausted all other avenues.</p>
|
||||
|
||||
|
|
@ -196,10 +189,6 @@ SearchMaxLengthendp
|
|||
|
||||
<div align="center">
|
||||
Graphics Programming Black Book © 2001 Michael Abrash
|
||||
</div><!-- all of the reference materials (books) have the footer and subfoot reveresed -->
|
||||
<!-- reference_subfoot = footer -->
|
||||
<!-- reference_footer = subfoot -->
|
||||
<!-- BEGIN SUB FOOTER -->
|
||||
<!-- END FOOTER -->
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
49
07-05.html
49
07-05.html
|
|
@ -10,16 +10,9 @@
|
|||
<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: Local Optimization</title><!-- HEADER -->
|
||||
<!-- Empty Reference Subhead -->
|
||||
<!--ISBN=1576101746//-->
|
||||
<!--TITLE=Michael Abrash's Graphics Programming Black Book Special Edition//-->
|
||||
<!--AUTHOR=Michael Abrash//-->
|
||||
<!--PUBLISHER=The Coriolis Group, Inc.//-->
|
||||
<title>Michael Abrash's Graphics Programming Black Book Special Edition: Local Optimization</title>
|
||||
<meta name="chapter" content="07" />
|
||||
<meta name="pages" content="145-148" />
|
||||
<!--UNASSIGNED1//-->
|
||||
<!--UNASSIGNED2//-->
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
|
@ -37,20 +30,20 @@
|
|||
|
||||
<p><br /></p>
|
||||
|
||||
<h4 align="left"><a id="Heading8"></a>Rotating and Shifting with Tables</h4>
|
||||
<h4 align="left" id="Heading8">Rotating and Shifting with Tables</h4>
|
||||
|
||||
<p>As another example of local optimization, consider the matter of rotating or shifting a mask into position. First, let’s look at the simple task of setting bit N of AX to 1.</p>
|
||||
|
||||
<p>The obvious way to do this is to place N in CL, rotate the bit into position, and OR it with AX, as follows:</p><!-- CODE SNIP //-->
|
||||
<p>The obvious way to do this is to place N in CL, rotate the bit into position, and OR it with AX, as follows:</p>
|
||||
<pre>
|
||||
MOV BX,1
|
||||
SHL BX,CL
|
||||
OR AX,BX
|
||||
</pre><!-- END CODE SNIP //-->
|
||||
</pre>
|
||||
|
||||
<p>This solution is obvious because it takes good advantage of the special ability of the x86 family to shift or rotate by the variable number of bits specified by CL. However, it takes an average of about 45 cycles on an 8088. It’s actually far faster to precalculate the results, pass the bit number in BX, and look the shifted bit up, as shown in Listing 7.3.</p>
|
||||
|
||||
<p><b>LISTING 7.3 L7-3.ASM</b></p><!-- CODE //-->
|
||||
<p><b>LISTING 7.3 L7-3.ASM</b></p>
|
||||
<pre>
|
||||
SHL BX,1 ;prepare for word sized look up
|
||||
OR AX,ShiftTable[BX] ;look up the bit and OR it in
|
||||
|
|
@ -61,13 +54,13 @@ BIT_PATTERN=0001H
|
|||
DW BIT_PATTERN
|
||||
BIT_PATTERN=BIT_PATTERN SHL 1
|
||||
ENDM
|
||||
</pre><!-- END CODE //-->
|
||||
</pre>
|
||||
|
||||
<p>Even though it accesses memory, this approach takes only 20 cycles—more than twice as fast as the variable shift. Once again, we were able to improve performance considerably—not by knowing the fastest instructions, but by selecting the fastest <i>sequence</i> of instructions.</p>
|
||||
|
||||
<p>In the particular example above, we once again run into the difficulty of optimizing across the x86 family. The table lookup is faster on the 8088 and 286, but it’s slightly slower on the 386 and no faster on the 486. However, 386/486-specific code could use enhanced addressing to accomplish the whole job in just one instruction, along the lines of the code snippet in Listing 7.4.</p>
|
||||
|
||||
<p><b>LISTING 7.4 L7-4.ASM</b></p><!-- CODE SNIP //-->
|
||||
<p><b>LISTING 7.4 L7-4.ASM</b></p>
|
||||
<pre>
|
||||
OR EAX,ShiftTable[EBX*4] ;look up the bit and OR it in
|
||||
:
|
||||
|
|
@ -77,7 +70,7 @@ BIT_PATTERN=0001H
|
|||
DD BIT_PATTERN
|
||||
BIT_PATTERN=BIT_PATTERN SHL 1
|
||||
ENDM
|
||||
</pre><!-- END CODE SNIP //-->
|
||||
</pre>
|
||||
|
||||
<table width="100%">
|
||||
<tr>
|
||||
|
|
@ -87,7 +80,7 @@ BIT_PATTERN=BIT_PATTERN SHL 1
|
|||
</tr>
|
||||
</table>
|
||||
|
||||
<h4 align="left"><a id="Heading9"></a>NOT Flips Bits—Not Flags</h4>
|
||||
<h4 align="left" id="Heading9">NOT Flips Bits—Not Flags</h4>
|
||||
|
||||
<p>The <b>NOT</b> instruction flips all the bits in the operand, from 0 to 1 or from 1 to 0. That’s as simple as could be, but <b>NOT</b> nonetheless has a minor but interesting talent: It doesn’t affect the flags. That can be irritating; I once spent a good hour tracking down a bug caused by my unconscious assumption that <b>NOT</b> does set the flags. After all, every other arithmetic and logical instruction sets the flags; why not <b>NOT</b>? Probably because <b>NOT</b> isn’t considered to be an arithmetic or logical instruction at all; rather, it’s a data manipulation instruction, like <b>MOV</b> and the various rotates. (These are <b>RCR, RCL, ROR,</b> and <b>ROL,</b> which affect only the Carry and Overflow flags.) NOT is often used for tasks, such as flipping masks, where there’s no reason to test the state of the result, and in that context it can be handy to keep the flags unmodified for later testing.</p>
|
||||
|
||||
|
|
@ -101,13 +94,13 @@ BIT_PATTERN=BIT_PATTERN SHL 1
|
|||
|
||||
<p>The x86 instruction set offers many ways to accomplish almost any task. Understanding the subtle distinctions between the instructions—whether and which flags are set, for example—can be critical when you’re trying to optimize a code sequence and you’re running out of registers, or when you’re trying to minimize branching.</p>
|
||||
|
||||
<h4 align="left"><a id="Heading10"></a>Incrementing with and without Carry</h4>
|
||||
<h4 align="left" id="Heading10">Incrementing with and without Carry</h4>
|
||||
|
||||
<p>Another case in which there are two slightly different ways to perform a task involves adding 1 to an operand. You can do this with <b>INC,</b> as in <b>INC AX,</b> or you can do it with <b>ADD,</b> as in <b>ADD AX,1.</b> What’s the difference? The obvious difference is that <b>INC</b> is usually a byte or two shorter (the exception being <b>ADD AL,1,</b> which at two bytes is the same length as <b>INC AL</b>), and is faster on some processors. Less obvious, but no less important, is that <b>ADD</b> sets the Carry flag while <b>INC</b> leaves the Carry flag untouched.</p>
|
||||
|
||||
<p>Why is that important? Because it allows <b>INC</b> to function as a data pointer manipulation instruction for multi-word arithmetic. You can use <b>INC</b> to advance the pointers in code like that shown in Listing 7.5 without having to do any work to preserve the Carry status from one addition to the next.</p>
|
||||
|
||||
<p><b>LISTING 7.5 L7-5.ASM</b></p><!-- CODE //-->
|
||||
<p><b>LISTING 7.5 L7-5.ASM</b></p>
|
||||
<pre>
|
||||
CLC ;clear the Carry for the initial addition
|
||||
LOOP_TOP:
|
||||
|
|
@ -118,11 +111,11 @@ LOOP_TOP:
|
|||
INC DI ;point to next dest operand word
|
||||
INC DI
|
||||
LOOP LOOP_TOP
|
||||
</pre><!-- END CODE //-->
|
||||
</pre>
|
||||
|
||||
<p>If <b>ADD</b> were used, the Carry flag would have to be saved between additions, with code along the lines shown in Listing 7.6.</p>
|
||||
|
||||
<p><b>LISTING 7.6 L7-6.ASM</b></p><!-- CODE //-->
|
||||
<p><b>LISTING 7.6 L7-6.ASM</b></p>
|
||||
<pre>
|
||||
CLC ;clear the carry for the initial addition
|
||||
LOOP_TOP:
|
||||
|
|
@ -133,7 +126,7 @@ LOOP_TOP:
|
|||
ADD DI,2 ;point to next dest operand word
|
||||
SAHF ;restore the carry flag
|
||||
LOOP LOOP_TOP
|
||||
</pre><!-- END CODE //-->
|
||||
</pre>
|
||||
|
||||
<p>It’s not that the Listing 7.6 approach is necessarily better or worse; that depends on the processor and the situation. The Listing 7.6 approach is <i>different,</i> and if you understand the differences, you’ll be able to choose the best approach for whatever code you happen to write. (<b>DEC</b> has the same property of preserving the Carry flag, by the way.)</p>
|
||||
|
||||
|
|
@ -151,17 +144,17 @@ LOOP_TOP:
|
|||
|
||||
<p>The other interesting point about the last example is the use of <b>LAHF</b> and <b>SAHF,</b> which transfer the low byte of the FLAGS register to and from AH, respectively. These instructions were created to help provide compatibility with the 8080’s (that’s <i>8080</i>, not <i>8088</i>) <b>PUSH</b> <b>PSW</b> and <b>POP PSW</b> instructions, but turn out to be compact (one byte) instructions for saving and restoring the arithmetic flags. A word of caution, however: <b>SAHF</b> restores the Carry, Zero, Sign, Auxiliary Carry, and Parity flags—but <i>not</i> the Overflow flag, which resides in the high byte of the FLAGS register. Also, be aware that <b>LAHF</b> and <b>SAHF</b> provide a fast way to preserve the flags on an 8088 but are relatively slow instructions on the 486 and Pentium.</p>
|
||||
|
||||
<p>There are times when it’s a clear liability that <b>INC</b> doesn’t set the Carry flag. For instance</p><!-- CODE SNIP //-->
|
||||
<p>There are times when it’s a clear liability that <b>INC</b> doesn’t set the Carry flag. For instance</p>
|
||||
<pre>
|
||||
INC AX
|
||||
ADC DX,0
|
||||
</pre><!-- END CODE SNIP //-->
|
||||
</pre>
|
||||
|
||||
<p>does <i>not</i> increment the 32-bit value in DX:AX. To do that, you’d need the following:</p><!-- CODE SNIP //-->
|
||||
<p>does <i>not</i> increment the 32-bit value in DX:AX. To do that, you’d need the following:</p>
|
||||
<pre>
|
||||
ADD AX,1
|
||||
ADC DX,0
|
||||
</pre><!-- END CODE SNIP //-->
|
||||
</pre>
|
||||
|
||||
<p>As always, pay attention!</p>
|
||||
|
||||
|
|
@ -182,10 +175,6 @@ ADC DX,0
|
|||
|
||||
<div align="center">
|
||||
Graphics Programming Black Book © 2001 Michael Abrash
|
||||
</div><!-- all of the reference materials (books) have the footer and subfoot reveresed -->
|
||||
<!-- reference_subfoot = footer -->
|
||||
<!-- reference_footer = subfoot -->
|
||||
<!-- BEGIN SUB FOOTER -->
|
||||
<!-- END FOOTER -->
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
23
08-01.html
23
08-01.html
|
|
@ -10,16 +10,9 @@
|
|||
<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: Speeding Up C with Assembly Language</title><!-- HEADER -->
|
||||
<!-- Empty Reference Subhead -->
|
||||
<!--ISBN=1576101746//-->
|
||||
<!--TITLE=Michael Abrash's Graphics Programming Black Book Special Edition//-->
|
||||
<!--AUTHOR=Michael Abrash//-->
|
||||
<!--PUBLISHER=The Coriolis Group, Inc.//-->
|
||||
<title>Michael Abrash's Graphics Programming Black Book Special Edition: Speeding Up C with Assembly Language</title>
|
||||
<meta name="chapter" content="08" />
|
||||
<meta name="pages" content="149-153" />
|
||||
<!--UNASSIGNED1//-->
|
||||
<!--UNASSIGNED2//-->
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
|
@ -37,10 +30,10 @@
|
|||
|
||||
<p><br /></p>
|
||||
|
||||
<h2><a id="Heading1"></a>Chapter 8<br />
|
||||
<h2 id="Heading1">Chapter 8<br />
|
||||
Speeding Up C with Assembly Language</h2>
|
||||
|
||||
<h3><a id="Heading2"></a>Jumping Languages When You Know It’ll Help</h3>
|
||||
<h3 id="Heading2">Jumping Languages When You Know It’ll Help</h3>
|
||||
|
||||
<p>When I was a senior in high school, a pop song called “Seasons in the Sun,” sung by one Terry Jacks, soared up the pop charts and spent, as best I can recall, two straight weeks atop <i>Kasey Kasem’s American Top 40.</i> “Seasons in the Sun” wasn’t a particularly good song, primarily because the lyrics were silly. I’ve never understood why the song was a hit, but, as so often happens with undistinguished but popular music by forgotten one- or two-shot groups (“Don’t Pull Your Love Out on Me Baby,” “Billy Don’t Be a Hero,” <i>et al.</i>), I heard it everywhere for a month or so, then gave it not another thought for 15 years.</p>
|
||||
|
||||
|
|
@ -62,7 +55,7 @@
|
|||
|
||||
<p>Apropos of which, when was the last time you heard of Terry Jacks?</p>
|
||||
|
||||
<h4 align="left"><a id="Heading3"></a>Billy, Don’t Be a Compiler</h4>
|
||||
<h4 align="left" id="Heading3">Billy, Don’t Be a Compiler</h4>
|
||||
|
||||
<p>The key to optimizing C programs with assembly language is, as always, writing good assembly language code, but with an added twist. Rule 1 when converting C code to assembly is this: <i>Don’t think like a compiler.</i> That’s more easily said than done, especially when the C code you’re converting is readily available as a model and the assembly code that the compiler generates is available as well. Nevertheless, the principle of not thinking like a compiler is essential, and is, in one form or another, the basis for all that I’ll discuss below.</p>
|
||||
|
||||
|
|
@ -80,7 +73,7 @@
|
|||
</tr>
|
||||
</table>
|
||||
|
||||
<h3><a id="Heading4"></a>Don’t Call Your Functions on Me, Baby</h3>
|
||||
<h3 id="Heading4">Don’t Call Your Functions on Me, Baby</h3>
|
||||
|
||||
<p>In order to think differently from a compiler, you must understand both what compilers and C programmers tend to do and how that differs from what assembly language does well. In this pursuit, it can be useful to examine the code your compiler generates, either by viewing the code in a debugger or by having the compiler generate an assembly language output file. (The latter is done with /Fa or /Fc in Microsoft C/C<small>++</small> and -S in Borland C<small>++</small>.)</p>
|
||||
|
||||
|
|
@ -105,10 +98,6 @@
|
|||
|
||||
<div align="center">
|
||||
Graphics Programming Black Book © 2001 Michael Abrash
|
||||
</div><!-- all of the reference materials (books) have the footer and subfoot reveresed -->
|
||||
<!-- reference_subfoot = footer -->
|
||||
<!-- reference_footer = subfoot -->
|
||||
<!-- BEGIN SUB FOOTER -->
|
||||
<!-- END FOOTER -->
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
34
08-02.html
34
08-02.html
|
|
@ -10,16 +10,9 @@
|
|||
<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: Speeding Up C with Assembly Language</title><!-- HEADER -->
|
||||
<!-- Empty Reference Subhead -->
|
||||
<!--ISBN=1576101746//-->
|
||||
<!--TITLE=Michael Abrash's Graphics Programming Black Book Special Edition//-->
|
||||
<!--AUTHOR=Michael Abrash//-->
|
||||
<!--PUBLISHER=The Coriolis Group, Inc.//-->
|
||||
<title>Michael Abrash's Graphics Programming Black Book Special Edition: Speeding Up C with Assembly Language</title>
|
||||
<meta name="chapter" content="08" />
|
||||
<meta name="pages" content="153-156" />
|
||||
<!--UNASSIGNED1//-->
|
||||
<!--UNASSIGNED2//-->
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
|
@ -37,13 +30,13 @@
|
|||
|
||||
<p><br /></p>
|
||||
|
||||
<h3><a id="Heading5"></a>Stack Frames Slow So Much</h3>
|
||||
<h3 id="Heading5">Stack Frames Slow So Much</h3>
|
||||
|
||||
<p>C compilers work within the stack frame model, whereby variables reside in a block of stack memory and are accessed via offsets from BP. Compilers may store a couple of variables in registers and may briefly keep other variables in registers when they’re used repeatedly, but the stack frame is the underlying architecture. It’s a nice architecture; it’s flexible, convenient, easy to program, and makes for fairly compact code. However, stack frames have a few drawbacks. They must be constructed and destroyed, which takes both time and code. They are so easy to use that they tend to bias the assembly language programmer in favor of accessing memory variables more often than might be necessary. Finally, you cannot use BP as a general-purpose register if you intend to access a stack frame, and having that seventh register available is sometimes useful indeed.</p>
|
||||
|
||||
<p>That doesn’t mean you shouldn’t use stack frames, which are useful and often necessary. Just don’t fall victim to their undeniable charms.</p>
|
||||
|
||||
<h3><a id="Heading6"></a>Torn Between Two Segments</h3>
|
||||
<h3 id="Heading6">Torn Between Two Segments</h3>
|
||||
|
||||
<p>C compilers are not terrific at handling segments. Some compilers can efficiently handle a single far pointer used in a loop by leaving ES set for the duration of the loop. But two far pointers used in the same loop confuse every compiler I’ve seen, causing the full segment:offset address to be reloaded each time either pointer is used.</p>
|
||||
|
||||
|
|
@ -57,7 +50,7 @@
|
|||
|
||||
<p>In assembly language you have full control over segments. Use it, and, if necessary, reorganize your code to minimize segment loading.</p>
|
||||
|
||||
<h4 align="left"><a id="Heading7"></a>Why Speeding Up Is Hard to Do</h4>
|
||||
<h4 align="left" id="Heading7">Why Speeding Up Is Hard to Do</h4>
|
||||
|
||||
<p>You might think that the most obvious advantage assembly language has over C is that it allows the use of all forms of instructions and all registers in all ways, whereas C compilers tend to use a subset of registers and instructions in a limited number of ways. Yes and no. It’s true that C compilers typically don’t generate instructions such as <b>XLAT,</b> rotates, or the string instructions. On the other hand, <b>XLAT</b> and rotates are useful in a limited set of circumstances, and string instructions <i>are</i> used in the C library functions. In fact, C library code is likely to be carefully optimized by experts, and may be much better than equivalent code you’d produce yourself.</p>
|
||||
|
||||
|
|
@ -75,21 +68,20 @@
|
|||
|
||||
<p>True optimization requires rethinking your code to take advantage of assembly language. A C loop that searches through an integer array for matches might compile</p>
|
||||
|
||||
<p><a id="Fig1"><!-- </A><A HREF="javascript:displayWindow('images/08-01.jpg',413,198 )"> --><img src="images/08-01.jpg" /><br />
|
||||
<!-- </A>
|
||||
<BR><A HREF="javascript:displayWindow('images/08-01.jpg',413,198)"> --><b>Figure 8.1</b></a> <i>Tweaked compiler output for a loop.</i></p>
|
||||
<p><a id="Fig1"><img src="images/08-01.jpg" /><br />
|
||||
<b>Figure 8.1</b></a> <i>Tweaked compiler output for a loop.</i></p>
|
||||
|
||||
<p>to something like Figure 8.1A. You might look at that and tweak it to the code shown in Figure 8.1B.</p>
|
||||
|
||||
<p>Congratulations! You’ve successfully eliminated all stack frame access, you’ve used <b>LOOP</b> (although <b>DEC SI/JNZ</b> is actually faster on 386 and later machines, as I explained in the last chapter), and you’ve used a string instruction. Unfortunately, the new code isn’t going to run very much faster. Maybe 25 percent faster, maybe a little more. Big deal. You’ve eliminated the trappings of the compiler—the stack frame and the restricted register usage—but you’re still <i>thinking</i> like the compiler. Try this:</p><!-- CODE SNIP //-->
|
||||
<p>Congratulations! You’ve successfully eliminated all stack frame access, you’ve used <b>LOOP</b> (although <b>DEC SI/JNZ</b> is actually faster on 386 and later machines, as I explained in the last chapter), and you’ve used a string instruction. Unfortunately, the new code isn’t going to run very much faster. Maybe 25 percent faster, maybe a little more. Big deal. You’ve eliminated the trappings of the compiler—the stack frame and the restricted register usage—but you’re still <i>thinking</i> like the compiler. Try this:</p>
|
||||
<pre>
|
||||
repnz scasw
|
||||
jz Match
|
||||
</pre><!-- END CODE SNIP //-->
|
||||
</pre>
|
||||
|
||||
<p>It’s a simple example—but, I hope, a convincing one. Stretch your brain when you optimize.</p>
|
||||
|
||||
<h3><a id="Heading8"></a>Taking It to the Limit</h3>
|
||||
<h3 id="Heading8">Taking It to the Limit</h3>
|
||||
|
||||
<p>The ultimate in assembly language optimization comes when you change the rules; that is, when you reorganize the entire program to allow the use of better assembly language code in the small section of code that most affects overall performance. For example, consider that the data searched in the last example is stored in an array of structures, with each structure in the array containing other information as well. In this situation, <b>REP SCASW</b> couldn’t be used because the data searched through wouldn’t be contiguous.</p>
|
||||
|
||||
|
|
@ -121,7 +113,7 @@ jz Match
|
|||
|
||||
<p>That said, let me show some of these precepts in action.</p>
|
||||
|
||||
<h4 align="left"><a id="Heading9"></a>A C-to-Assembly Case Study</h4>
|
||||
<h4 align="left" id="Heading9">A C-to-Assembly Case Study</h4>
|
||||
|
||||
<p>Listing 8.1 is the sample C application I’m going to use to examine optimization in action. Listing 8.1 isn’t really complete—it doesn’t handle the “no-matches” case well, and it assumes that the sum of all matches will fit into an <b>int—</b>but it will do just fine as an optimization example.</p>
|
||||
|
||||
|
|
@ -142,10 +134,6 @@ jz Match
|
|||
|
||||
<div align="center">
|
||||
Graphics Programming Black Book © 2001 Michael Abrash
|
||||
</div><!-- all of the reference materials (books) have the footer and subfoot reveresed -->
|
||||
<!-- reference_subfoot = footer -->
|
||||
<!-- reference_footer = subfoot -->
|
||||
<!-- BEGIN SUB FOOTER -->
|
||||
<!-- END FOOTER -->
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
28
08-03.html
28
08-03.html
|
|
@ -10,16 +10,9 @@
|
|||
<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: Speeding Up C with Assembly Language</title><!-- HEADER -->
|
||||
<!-- Empty Reference Subhead -->
|
||||
<!--ISBN=1576101746//-->
|
||||
<!--TITLE=Michael Abrash's Graphics Programming Black Book Special Edition//-->
|
||||
<!--AUTHOR=Michael Abrash//-->
|
||||
<!--PUBLISHER=The Coriolis Group, Inc.//-->
|
||||
<title>Michael Abrash's Graphics Programming Black Book Special Edition: Speeding Up C with Assembly Language</title>
|
||||
<meta name="chapter" content="08" />
|
||||
<meta name="pages" content="156-160" />
|
||||
<!--UNASSIGNED1//-->
|
||||
<!--UNASSIGNED2//-->
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
|
@ -37,7 +30,7 @@
|
|||
|
||||
<p><br /></p>
|
||||
|
||||
<p><b>LISTING 8.1 L8-1.C</b></p><!-- CODE //-->
|
||||
<p><b>LISTING 8.1 L8-1.C</b></p>
|
||||
<pre>
|
||||
/* Program to search an array spanning a linked list of variable-
|
||||
sized blocks, for all entries with a specified ID number,
|
||||
|
|
@ -158,17 +151,16 @@ unsigned int FindIDAverage(unsigned int SearchedForID,
|
|||
else
|
||||
return(IDMatchSum / IDMatchCount);
|
||||
}
|
||||
</pre><!-- END CODE //-->
|
||||
</pre>
|
||||
|
||||
<p>The main body of Listing 8.1 constructs a linked list of memory blocks of various sizes and stores an array of structures across those blocks, as shown in Figure 8.2. The function <b>FindIDAverage</b> in Listing 8.1 searches through that array for all matches to a specified ID number and returns the average value of all such matches. <b>FindIDAverage</b> contains two nested loops, the outer one repeating once for each linked block and the inner one repeating once for each array element in each block. The inner loop—the critical one—is compact, containing only four statements, and should lend itself rather well to compiler optimization.</p>
|
||||
|
||||
<p><a id="Fig2"><!-- </A><A HREF="javascript:displayWindow('images/08-02.jpg',413,265 )"> --><img src="images/08-02.jpg" /><br />
|
||||
<!-- </A>
|
||||
<BR><A HREF="javascript:displayWindow('images/08-02.jpg',413,265)"> --><b>Figure 8.2</b></a> <i>Linked array storage format (version 1).</i></p>
|
||||
<p><a id="Fig2"><img src="images/08-02.jpg" /><br />
|
||||
<b>Figure 8.2</b></a> <i>Linked array storage format (version 1).</i></p>
|
||||
|
||||
<p>As it happens, Microsoft C/C<small>++</small> does optimize the inner loop of <b>FindIDAverage</b> nicely. Listing 8.2 shows the code Microsoft C/C<small>++</small> generates for the inner loop, consisting of a mere seven assembly language instructions inside the loop. The compiler is smart enough to convert the loop index variable, which counts up but is used for nothing but counting loops, into a count-down variable so that the <b>LOOP</b> instruction can be used.</p>
|
||||
|
||||
<p><b>LISTING 8.2 L8-2.COD</b></p><!-- CODE //-->
|
||||
<p><b>LISTING 8.2 L8-2.COD</b></p>
|
||||
<pre>
|
||||
; Code generated by Microsoft C for inner loop of FindIDAverage.
|
||||
;|*** for (WorkingBlockCount=0;
|
||||
|
|
@ -199,7 +191,7 @@ $I265:
|
|||
mov WORD PTR [bp-2],di ;IDMatchSum
|
||||
mov WORD PTR [bp-4],dx ;IDMatchCount
|
||||
$FB264:
|
||||
</pre><!-- END CODE //-->
|
||||
</pre>
|
||||
|
||||
<p><br /></p>
|
||||
|
||||
|
|
@ -218,10 +210,6 @@ $FB264:
|
|||
|
||||
<div align="center">
|
||||
Graphics Programming Black Book © 2001 Michael Abrash
|
||||
</div><!-- all of the reference materials (books) have the footer and subfoot reveresed -->
|
||||
<!-- reference_subfoot = footer -->
|
||||
<!-- reference_footer = subfoot -->
|
||||
<!-- BEGIN SUB FOOTER -->
|
||||
<!-- END FOOTER -->
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
27
08-04.html
27
08-04.html
|
|
@ -10,16 +10,9 @@
|
|||
<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: Speeding Up C with Assembly Language</title><!-- HEADER -->
|
||||
<!-- Empty Reference Subhead -->
|
||||
<!--ISBN=1576101746//-->
|
||||
<!--TITLE=Michael Abrash's Graphics Programming Black Book Special Edition//-->
|
||||
<!--AUTHOR=Michael Abrash//-->
|
||||
<!--PUBLISHER=The Coriolis Group, Inc.//-->
|
||||
<title>Michael Abrash's Graphics Programming Black Book Special Edition: Speeding Up C with Assembly Language</title>
|
||||
<meta name="chapter" content="08" />
|
||||
<meta name="pages" content="160-163" />
|
||||
<!--UNASSIGNED1//-->
|
||||
<!--UNASSIGNED2//-->
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
|
@ -39,7 +32,7 @@
|
|||
|
||||
<p>It’s hard to squeeze much more performance from this code by tweaking it, as exemplified by Listing 8.3, a fine-tuned assembly version of <b>FindIDAverage</b> that was produced by looking at the assembly output of MS C/C<small>++</small> and tightening it. Listing 8.3 eliminates all stack frame access in the inner loop, but that’s about all the tightening there is to do. The result, as shown in Table 8.1, is that Listing 8.3 runs a modest 11 percent faster than Listing 8.1 on a 386. The results could vary considerably, depending on the nature of the data set searched through (average block size and frequency of matches). But, then, understanding the typical and worst case conditions is part of optimization, isn’t it?</p>
|
||||
|
||||
<p><b>LISTING 8.3 L8-3.ASM</b></p><!-- CODE //-->
|
||||
<p><b>LISTING 8.3 L8-3.ASM</b></p>
|
||||
<pre>
|
||||
; Typically optimized assembly language version of FindIDAverage.
|
||||
SearchedForID equ 4 ;Passed parameter offsets in the
|
||||
|
|
@ -54,7 +47,7 @@ DATA_ELEMENT_SIZE equ 4 ;Number of bytes in struct DataElement
|
|||
.model small
|
||||
.code
|
||||
public _FindIDAverage
|
||||
</pre><!-- END CODE //-->
|
||||
</pre>
|
||||
|
||||
<table width="100%">
|
||||
<tr>
|
||||
|
|
@ -156,7 +149,7 @@ DATA_ELEMENT_SIZE equ 4 ;Number of bytes in struct DataElement
|
|||
<hr />
|
||||
</td>
|
||||
</tr>
|
||||
</table><!-- CODE //-->
|
||||
</table>
|
||||
<pre>
|
||||
_FindIDAverage proc near
|
||||
push bp ;Save caller’s stack frame
|
||||
|
|
@ -201,11 +194,11 @@ Done: pop si ;Restore C register variables
|
|||
ret
|
||||
_FindIDAverage ENDP
|
||||
end
|
||||
</pre><!-- END CODE //-->
|
||||
</pre>
|
||||
|
||||
<p>Listing 8.4 tosses some sophisticated optimization techniques into the mix. The loop is unrolled eight times, eliminating a good deal of branching, and <b>SCASW</b> is used instead of <b>CMP [DI],AX.</b> (Note, however, that <b>SCASW</b> is in fact slower than <b>CMP [DI],AX</b> on the 386 and 486, and is sometimes faster on the 286 and 8088 only because it’s shorter and therefore may prefetch faster.) This advanced tweaking produces a 39 percent improvement over the original C code—substantial, but not a tremendous return for the optimization effort invested.</p>
|
||||
|
||||
<p><b>LISTING 8.4 L8-4.ASM</b></p><!-- CODE //-->
|
||||
<p><b>LISTING 8.4 L8-4.ASM</b></p>
|
||||
<pre>
|
||||
; Heavily optimized assembly language version of FindIDAverage.
|
||||
; Features an unrolled loop and more efficient pointer use.
|
||||
|
|
@ -295,7 +288,7 @@ Done: pop si ;Restore C register variables
|
|||
ret
|
||||
_FindIDAverage ENDP
|
||||
end
|
||||
</pre><!-- END CODE //-->
|
||||
</pre>
|
||||
|
||||
<p><br /></p>
|
||||
|
||||
|
|
@ -314,10 +307,6 @@ _FindIDAverage ENDP
|
|||
|
||||
<div align="center">
|
||||
Graphics Programming Black Book © 2001 Michael Abrash
|
||||
</div><!-- all of the reference materials (books) have the footer and subfoot reveresed -->
|
||||
<!-- reference_subfoot = footer -->
|
||||
<!-- reference_footer = subfoot -->
|
||||
<!-- BEGIN SUB FOOTER -->
|
||||
<!-- END FOOTER -->
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
30
08-05.html
30
08-05.html
|
|
@ -10,16 +10,9 @@
|
|||
<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: Speeding Up C with Assembly Language</title><!-- HEADER -->
|
||||
<!-- Empty Reference Subhead -->
|
||||
<!--ISBN=1576101746//-->
|
||||
<!--TITLE=Michael Abrash's Graphics Programming Black Book Special Edition//-->
|
||||
<!--AUTHOR=Michael Abrash//-->
|
||||
<!--PUBLISHER=The Coriolis Group, Inc.//-->
|
||||
<title>Michael Abrash's Graphics Programming Black Book Special Edition: Speeding Up C with Assembly Language</title>
|
||||
<meta name="chapter" content="08" />
|
||||
<meta name="pages" content="163-166" />
|
||||
<!--UNASSIGNED1//-->
|
||||
<!--UNASSIGNED2//-->
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
|
@ -39,7 +32,7 @@
|
|||
|
||||
<p>Listings 8.5 and 8.6 together go the final step and change the rules in favor of assembly language. Listing 8.5 creates the same list of linked blocks as Listing 8.1. However, instead of storing an array of structures within each block, it stores <i>two</i> arrays in each block, one consisting of ID numbers and the other consisting of the corresponding values, as shown in Figure 8.3. No information is lost; the data is merely rearranged.</p>
|
||||
|
||||
<p><b>LISTING 8.5 L8-5.C</b></p><!-- CODE //-->
|
||||
<p><b>LISTING 8.5 L8-5.C</b></p>
|
||||
<pre>
|
||||
/* Program to search an array spanning a linked list of variable-
|
||||
sized blocks, for all entries with a specified ID number,
|
||||
|
|
@ -59,11 +52,10 @@ void main(void);
|
|||
void exit(int);
|
||||
extern unsigned int FindIDAverage2(unsigned int,
|
||||
struct BlockHeader *);
|
||||
</pre><!-- END CODE //-->
|
||||
</pre>
|
||||
|
||||
<p><a id="Fig3"><!-- </A><A HREF="javascript:displayWindow('images/08-03.jpg',413,261 )"> --><img src="images/08-03.jpg" /><br />
|
||||
<!-- </A>
|
||||
<BR><A HREF="javascript:displayWindow('images/08-03.jpg',413,261)"> --><b>Figure 8.3</b></a> <i>Linked array storage format (version 2).</i></p><!-- CODE //-->
|
||||
<p><a id="Fig3"><img src="images/08-03.jpg" /><br />
|
||||
<b>Figure 8.3</b></a> <i>Linked array storage format (version 2).</i></p>
|
||||
<pre>
|
||||
/* Structure that starts each variable-sized block */
|
||||
struct BlockHeader {
|
||||
|
|
@ -117,9 +109,9 @@ void main(void) {
|
|||
IDToFind, FindIDAverage2(IDToFind, BaseArrayBlockPointer));
|
||||
exit(0);
|
||||
}
|
||||
</pre><!-- END CODE //-->
|
||||
</pre>
|
||||
|
||||
<p><b>LISTING 8.6 L8-6.ASM</b></p><!-- CODE //-->
|
||||
<p><b>LISTING 8.6 L8-6.ASM</b></p>
|
||||
<pre>
|
||||
; Alternative optimized assembly language version of FindIDAverage
|
||||
; requires data organized as two arrays within each block rather
|
||||
|
|
@ -188,7 +180,7 @@ Done: pop si ;Restore C register variables
|
|||
ret
|
||||
_FindIDAverage2 ENDP
|
||||
end
|
||||
</pre><!-- END CODE //-->
|
||||
</pre>
|
||||
|
||||
<p>The whole point of this rearrangement is to allow us to use <b>REP SCASW</b> to search through each block, and that’s exactly what <b>FindIDAverage2</b> in Listing 8.6 does. The result: Listing 8.6 calculates the average about <i>three times</i> as fast as the original C implementation and more than twice as fast as Listing 8.4, heavily optimized as the latter code is.</p>
|
||||
|
||||
|
|
@ -211,10 +203,6 @@ _FindIDAverage2 ENDP
|
|||
|
||||
<div align="center">
|
||||
Graphics Programming Black Book © 2001 Michael Abrash
|
||||
</div><!-- all of the reference materials (books) have the footer and subfoot reveresed -->
|
||||
<!-- reference_subfoot = footer -->
|
||||
<!-- reference_footer = subfoot -->
|
||||
<!-- BEGIN SUB FOOTER -->
|
||||
<!-- END FOOTER -->
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
55
09-01.html
55
09-01.html
|
|
@ -10,16 +10,9 @@
|
|||
<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: Hints My Readers Gave Me</title><!-- HEADER -->
|
||||
<!-- Empty Reference Subhead -->
|
||||
<!--ISBN=1576101746//-->
|
||||
<!--TITLE=Michael Abrash's Graphics Programming Black Book Special Edition//-->
|
||||
<!--AUTHOR=Michael Abrash//-->
|
||||
<!--PUBLISHER=The Coriolis Group, Inc.//-->
|
||||
<title>Michael Abrash's Graphics Programming Black Book Special Edition: Hints My Readers Gave Me</title>
|
||||
<meta name="chapter" content="09" />
|
||||
<meta name="pages" content="167-172" />
|
||||
<!--UNASSIGNED1//-->
|
||||
<!--UNASSIGNED2//-->
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
|
@ -37,19 +30,19 @@
|
|||
|
||||
<p><br /></p>
|
||||
|
||||
<h2><a id="Heading1"></a>Chapter 9<br />
|
||||
<h2 id="Heading1">Chapter 9<br />
|
||||
Hints My Readers Gave Me</h2>
|
||||
|
||||
<h3><a id="Heading2"></a>Optimization Odds and Ends from the Field</h3>
|
||||
<h3 id="Heading2">Optimization Odds and Ends from the Field</h3>
|
||||
|
||||
<p>Back in high school, I took a pre-calculus class from Mr. Bourgeis, whose most notable characteristics were incessant pacing and truly enormous feet. My friend Barry, who sat in the back row, right behind me, claimed that it was because of his large feet that Mr. Bourgeis was so restless. Those feet were <i>so</i> heavy, Barry hypothesized, that if Mr. Bourgeis remained in any one place for too long, the floor would give way under the strain, plunging the unfortunate teacher deep into the mantle of the Earth and possibly all the way through to China. Many amusing cartoons were drawn to this effect.</p>
|
||||
|
||||
<p>Unfortunately, Barry was too busy drawing cartoons, or, alternatively, sleeping, to actually learn any math. In the long run, that didn’t turn out to be a handicap for Barry, who went on to become vice-president of sales for a ham-packing company, where presumably he was rarely called upon to derive the quadratic equation. Barry’s lack of scholarship caused some problems back then, though. On one memorable occasion, Barry was half-asleep, with his eyes open but unfocused and his chin balanced on his hand in the classic “if I fall asleep my head will fall off my hand and I’ll wake up” posture, when Mr. Bourgeis popped a killer problem:</p>
|
||||
|
||||
<p>“Barry, solve this for X, please.” On the blackboard lay the equation:</p><!-- CODE SNIP //-->
|
||||
<p>“Barry, solve this for X, please.” On the blackboard lay the equation:</p>
|
||||
<pre>
|
||||
X - 1 = 0
|
||||
</pre><!-- END CODE SNIP //-->
|
||||
</pre>
|
||||
|
||||
<p>“Minus 1,” Barry said promptly.</p>
|
||||
|
||||
|
|
@ -71,23 +64,23 @@ X - 1 = 0
|
|||
|
||||
<p>I like to think I know more about performance programming than Barry knew about math. Nonetheless, I always welcome good ideas and comments, and many readers have sent me a slew of those over the years. So in this chapter, I think I’ll return the favor by devoting a chapter to reader feedback.</p>
|
||||
|
||||
<h4 align="left"><a id="Heading3"></a>Another Look at LEA</h4>
|
||||
<h4 align="left" id="Heading3">Another Look at LEA</h4>
|
||||
|
||||
<p>Several people have pointed out that while <b>LEA</b> is great for performing certain additions (see Chapter 6), it isn’t a perfect replacement for <b>ADD</b>. What’s the difference? <b>LEA</b>, an addressing instruction by trade, doesn’t affect the flags, while the arithmetic <b>ADD</b> instruction most certainly does. This is no problem when performing additions that involve only quantities that fit in one machine word (32 bits in 386 protected mode, 16 bits otherwise), but it renders <b>LEA</b> useless for multiword operations, which use the Carry flag to tie together partial results. For example, these instructions</p><!-- CODE SNIP //-->
|
||||
<p>Several people have pointed out that while <b>LEA</b> is great for performing certain additions (see Chapter 6), it isn’t a perfect replacement for <b>ADD</b>. What’s the difference? <b>LEA</b>, an addressing instruction by trade, doesn’t affect the flags, while the arithmetic <b>ADD</b> instruction most certainly does. This is no problem when performing additions that involve only quantities that fit in one machine word (32 bits in 386 protected mode, 16 bits otherwise), but it renders <b>LEA</b> useless for multiword operations, which use the Carry flag to tie together partial results. For example, these instructions</p>
|
||||
<pre>
|
||||
ADD EAX,EBX
|
||||
ADC EDX,ECX
|
||||
</pre><!-- END CODE SNIP //-->
|
||||
</pre>
|
||||
|
||||
<p>could <i>not</i> be replaced</p><!-- CODE SNIP //-->
|
||||
<p>could <i>not</i> be replaced</p>
|
||||
<pre>
|
||||
LEA EAX,[EAX+EBX]
|
||||
ADC EDX,ECX
|
||||
</pre><!-- END CODE SNIP //-->
|
||||
</pre>
|
||||
|
||||
<p>because <b>LEA</b> doesn’t affect the Carry flag.</p>
|
||||
|
||||
<p>The no-carry characteristic of <b>LEA</b> becomes a distinct advantage when performing pointer arithmetic, however. For instance, the following code uses <b>LEA</b> to advance the pointers while adding one 128-bit memory variable to another such variable:</p><!-- CODE //-->
|
||||
<p>The no-carry characteristic of <b>LEA</b> becomes a distinct advantage when performing pointer arithmetic, however. For instance, the following code uses <b>LEA</b> to advance the pointers while adding one 128-bit memory variable to another such variable:</p>
|
||||
<pre>
|
||||
MOV ECX,4 ;# of 32-bit words to add
|
||||
CLC
|
||||
|
|
@ -99,7 +92,7 @@ ADDLOOP:
|
|||
LEA ESI,[ESI+4] ;advance one array’s pointer
|
||||
LEA EDI,[EDI+4] ;advance the other array’s pointer
|
||||
LOOP ADDLOOP
|
||||
</pre><!-- END CODE //-->
|
||||
</pre>
|
||||
|
||||
<p>(Yes, I could use <b>LODSD</b> instead of <b>MOV/LEA</b>; I’m just illustrating a point here. Besides, <b>LODS</b> is only 1 cycle faster than <b>MOV/LEA</b> on the 386, and is actually more than twice as slow on the 486.) If we used <b>ADD</b> rather than <b>LEA</b> to advance the pointers, the carry from one <b>ADC</b> to the next would have to be preserved with either <b>PUSHF/POPF</b> or <b>LAHF/SAHF</b>. (Alternatively, we could use multiple <b>INC</b>s, since <b>INC</b> doesn’t affect the Carry flag.)</p>
|
||||
|
||||
|
|
@ -107,37 +100,37 @@ ADDLOOP:
|
|||
|
||||
<p>But there sure are a lot of interesting options, aren’t there?</p>
|
||||
|
||||
<h4 align="left"><a id="Heading4"></a>The Kennedy Portfolio</h4>
|
||||
<h4 align="left" id="Heading4">The Kennedy Portfolio</h4>
|
||||
|
||||
<p>Reader John Kennedy regularly passes along intriguing assembly programming tricks, many of which I’ve never seen mentioned anywhere else. John likes to optimize for size, whereas I lean more toward speed, but many of his optimizations are good for both purposes. Here are a few of my favorites:</p>
|
||||
|
||||
<p>John’s code for setting AX to its absolute value is:</p><!-- CODE SNIP //-->
|
||||
<p>John’s code for setting AX to its absolute value is:</p>
|
||||
<pre>
|
||||
CWD
|
||||
XOR AX,DX
|
||||
SUB AX,DX
|
||||
</pre><!-- END CODE SNIP //-->
|
||||
</pre>
|
||||
|
||||
<p>This does nothing when bit 15 of AX is 0 (that is, if AX is positive). When AX is negative, the code “nots” it and adds 1, which is exactly how you perform a two’s complement negate. For the case where AX is not negative, this trick usually beats the stuffing out of the standard absolute value code:</p><!-- CODE SNIP //-->
|
||||
<p>This does nothing when bit 15 of AX is 0 (that is, if AX is positive). When AX is negative, the code “nots” it and adds 1, which is exactly how you perform a two’s complement negate. For the case where AX is not negative, this trick usually beats the stuffing out of the standard absolute value code:</p>
|
||||
<pre>
|
||||
AND AX,AX ;negative?
|
||||
JNS IsPositive ;no
|
||||
NEG AX ;yes,negate it
|
||||
IsPositive:
|
||||
</pre><!-- END CODE SNIP //-->
|
||||
</pre>
|
||||
|
||||
<p>However, John’s code is slower on a 486; as you’re no doubt coming to realize (and as I’ll explain in Chapters 12 and 13), the 486 is an optimization world unto itself.</p>
|
||||
|
||||
<p>Here’s how John copies a block of bytes from DS:SI to ES:DI, moving as much data as possible a word at a time:</p><!-- CODE SNIP //-->
|
||||
<p>Here’s how John copies a block of bytes from DS:SI to ES:DI, moving as much data as possible a word at a time:</p>
|
||||
<pre>
|
||||
SHR CX,1 ;word count
|
||||
REP MOVSW ;copy as many words as possible
|
||||
ADC CX,CX ;CX=1 if copy length was odd,
|
||||
;0 else
|
||||
REP MOVSB ;copy any odd byte
|
||||
</pre><!-- END CODE SNIP //-->
|
||||
</pre>
|
||||
|
||||
<p>(<b>ADC CX,CX</b> can be replaced with <b>RCL CX,1</b>; which is faster depends on the processor type.) It might be hard to believe that the above is faster than this:</p><!-- CODE SNIP //-->
|
||||
<p>(<b>ADC CX,CX</b> can be replaced with <b>RCL CX,1</b>; which is faster depends on the processor type.) It might be hard to believe that the above is faster than this:</p>
|
||||
<pre>
|
||||
SHR CX,1 ;word count
|
||||
REP MOVSW ;copy as many words as
|
||||
|
|
@ -145,7 +138,7 @@ REP MOVSB ;copy any odd byte
|
|||
JNC CopyDone ;done if even copy length
|
||||
MOVSB ;copy the odd byte
|
||||
CopyDone:
|
||||
</pre><!-- END CODE SNIP //-->
|
||||
</pre>
|
||||
|
||||
<p><br /></p>
|
||||
|
||||
|
|
@ -164,10 +157,6 @@ CopyDone:
|
|||
|
||||
<div align="center">
|
||||
Graphics Programming Black Book © 2001 Michael Abrash
|
||||
</div><!-- all of the reference materials (books) have the footer and subfoot reveresed -->
|
||||
<!-- reference_subfoot = footer -->
|
||||
<!-- reference_footer = subfoot -->
|
||||
<!-- BEGIN SUB FOOTER -->
|
||||
<!-- END FOOTER -->
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
40
09-02.html
40
09-02.html
|
|
@ -10,16 +10,9 @@
|
|||
<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: Hints My Readers Gave Me</title><!-- HEADER -->
|
||||
<!-- Empty Reference Subhead -->
|
||||
<!--ISBN=1576101746//-->
|
||||
<!--TITLE=Michael Abrash's Graphics Programming Black Book Special Edition//-->
|
||||
<!--AUTHOR=Michael Abrash//-->
|
||||
<!--PUBLISHER=The Coriolis Group, Inc.//-->
|
||||
<title>Michael Abrash's Graphics Programming Black Book Special Edition: Hints My Readers Gave Me</title>
|
||||
<meta name="chapter" content="09" />
|
||||
<meta name="pages" content="172-175" />
|
||||
<!--UNASSIGNED1//-->
|
||||
<!--UNASSIGNED2//-->
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
|
@ -39,7 +32,7 @@
|
|||
|
||||
<p>However, it generally is. Sure, if the length is odd, John’s approach incurs a penalty approximately equal to the <b>REP</b> startup time for <b>MOVSB</b>. However, if the length is even, John’s approach doesn’t branch, saving cycles and not emptying the prefetch queue. If copy lengths are evenly distributed between even and odd, John’s approach is faster in most x86 systems. (Not on the 486, though.)</p>
|
||||
|
||||
<p>John also points out that on the 386, multiple <b>LEA</b>s can be combined to perform multiplications that can’t be handled by a single <b>LEA</b>, much as multiple shifts and adds can be used for multiplication, only faster. <b>LEA</b> can be used to multiply in a single instruction on the 386, but only by the values 2, 3, 4, 5, 8, and 9; several <b>LEA</b>s strung together can handle a much wider range of values. For example, video programmers are undoubtedly familiar with the following code to multiply AX times 80 (the width in bytes of the bitmap in most PC display modes):</p><!-- CODE SNIP //-->
|
||||
<p>John also points out that on the 386, multiple <b>LEA</b>s can be combined to perform multiplications that can’t be handled by a single <b>LEA</b>, much as multiple shifts and adds can be used for multiplication, only faster. <b>LEA</b> can be used to multiply in a single instruction on the 386, but only by the values 2, 3, 4, 5, 8, and 9; several <b>LEA</b>s strung together can handle a much wider range of values. For example, video programmers are undoubtedly familiar with the following code to multiply AX times 80 (the width in bytes of the bitmap in most PC display modes):</p>
|
||||
<pre>
|
||||
SHL AX,1 ;*2
|
||||
SH LAX,1 ;*4
|
||||
|
|
@ -49,31 +42,31 @@ MO VBX,AX
|
|||
SH LAX,1 ;*32
|
||||
SH LAX,1 ;*64
|
||||
ADD AX,BX ;*80
|
||||
</pre><!-- END CODE SNIP //-->
|
||||
</pre>
|
||||
|
||||
<p>Using <b>LEA</b> on the 386, the above could be reduced to</p><!-- CODE SNIP //-->
|
||||
<p>Using <b>LEA</b> on the 386, the above could be reduced to</p>
|
||||
<pre>
|
||||
LEA EAX,[EAX*2] ;*2
|
||||
LEA EAX,[EAX*8] ;*16
|
||||
LEA EAX,[EAX+EAX*4] ;*80
|
||||
</pre><!-- END CODE SNIP //-->
|
||||
</pre>
|
||||
|
||||
<p>which still isn’t as fast as using a lookup table like</p><!-- CODE SNIP //-->
|
||||
<p>which still isn’t as fast as using a lookup table like</p>
|
||||
<pre>
|
||||
MOV EAX,MultiplesOf80Table[EAX*4]
|
||||
</pre><!-- END CODE SNIP //-->
|
||||
</pre>
|
||||
|
||||
<p>but is close and takes a great deal less space.</p>
|
||||
|
||||
<p>Of course, on the 386, the shift and add version could also be reduced to this considerably more efficient code:</p><!-- CODE SNIP //-->
|
||||
<p>Of course, on the 386, the shift and add version could also be reduced to this considerably more efficient code:</p>
|
||||
<pre>
|
||||
SH LAX,4 ;*16
|
||||
MOV BX,AX
|
||||
SHL AX,2 ;*64
|
||||
ADD AX,BX ;*80
|
||||
</pre><!-- END CODE SNIP //-->
|
||||
</pre>
|
||||
|
||||
<h4 align="left"><a id="Heading5"></a>Speeding Up Multiplication</h4>
|
||||
<h4 align="left" id="Heading5">Speeding Up Multiplication</h4>
|
||||
|
||||
<p>That brings us to multiplication, one of the slowest of x86 operations and one that allows for considerable optimization. One way to speed up multiplication is to use shift and add, <b>LEA</b>, or a lookup table to hard-code a multiplication operation for a fixed multiplier, as shown above. Another is to take advantage of the early-out feature of the 386 (and the 486, but in the interests of brevity I’ll just say “386” from now on) by arranging your operands so that the multiplier (always the rightmost operand following <b>MUL</b> or <b>IMUL</b>) is no larger than the other operand.</p>
|
||||
|
||||
|
|
@ -103,13 +96,12 @@ ADD AX,BX ;*80
|
|||
|
||||
<p>That doesn’t mean that your code should test and swap operands to make sure the smaller one is the multiplier; that rarely pays off. I’m speaking more of the case where you’re scaling an array up by a value that’s always in the range of, say, 2 to 10; because the scale value will always be small and the array elements may have any value, the scale value is the logical choice for the multiplier.</p>
|
||||
|
||||
<h4 align="left"><a id="Heading6"></a>Optimizing Optimized Searching</h4>
|
||||
<h4 align="left" id="Heading6">Optimizing Optimized Searching</h4>
|
||||
|
||||
<p>Rob Williams writes with a wonderful optimization to the <b>REPNZ SCASB-</b>based optimized searching routine I discussed in Chapter 5. As a quick refresher, I described searching a buffer for a text string as follows: Scan for the first byte of the text string with <b>REPNZ SCASB</b>, then use <b>REPZ CMPS</b> to check for a full match whenever <b>REPNZ SCASB</b> finds a match for the first character, as shown in Figure 9.1. The principle is that most buffer characters won’t match the first character of any given string, so <b>REPNZ SCASB</b>, by far the fastest way to search on the PC, can be used to eliminate most potential matches; each remaining potential match can then be checked in its entirety with <b>REPZ CMPS</b>.</p>
|
||||
|
||||
<p><a id="Fig1"><!-- </A><A HREF="javascript:displayWindow('images/09-01.jpg',406,306 )"> --><img src="images/09-01.jpg" /><br />
|
||||
<!-- </A>
|
||||
<BR><A HREF="javascript:displayWindow('images/09-01.jpg',406,306)"> --><b>Figure 9.1</b></a> <i>Simple searching method for locating a text string.</i></p>
|
||||
<p><a id="Fig1"><img src="images/09-01.jpg" /><br />
|
||||
<b>Figure 9.1</b></a> <i>Simple searching method for locating a text string.</i></p>
|
||||
|
||||
<p>Rob’s revelation, which he credits without explanation to Edgar Allen Poe (search nevermore?), was that by far the slowest part of the whole deal is handling <b>REPNZ SCASB</b> matches, which require checking the remainder of the string with <b>REPZ CMPS</b> and restarting <b>REPNZ SCASB</b> if no match is found.</p>
|
||||
|
||||
|
|
@ -140,10 +132,6 @@ ADD AX,BX ;*80
|
|||
|
||||
<div align="center">
|
||||
Graphics Programming Black Book © 2001 Michael Abrash
|
||||
</div><!-- all of the reference materials (books) have the footer and subfoot reveresed -->
|
||||
<!-- reference_subfoot = footer -->
|
||||
<!-- reference_footer = subfoot -->
|
||||
<!-- BEGIN SUB FOOTER -->
|
||||
<!-- END FOOTER -->
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
24
09-03.html
24
09-03.html
|
|
@ -10,16 +10,9 @@
|
|||
<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: Hints My Readers Gave Me</title><!-- HEADER -->
|
||||
<!-- Empty Reference Subhead -->
|
||||
<!--ISBN=1576101746//-->
|
||||
<!--TITLE=Michael Abrash's Graphics Programming Black Book Special Edition//-->
|
||||
<!--AUTHOR=Michael Abrash//-->
|
||||
<!--PUBLISHER=The Coriolis Group, Inc.//-->
|
||||
<title>Michael Abrash's Graphics Programming Black Book Special Edition: Hints My Readers Gave Me</title>
|
||||
<meta name="chapter" content="09" />
|
||||
<meta name="pages" content="175-178" />
|
||||
<!--UNASSIGNED1//-->
|
||||
<!--UNASSIGNED2//-->
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
|
@ -49,11 +42,10 @@
|
|||
</tr>
|
||||
</table>
|
||||
|
||||
<p><a id="Fig2"><!-- </A><A HREF="javascript:displayWindow('images/09-02.jpg',409,306 )"> --><img src="images/09-02.jpg" /><br />
|
||||
<!-- </A>
|
||||
<BR><A HREF="javascript:displayWindow('images/09-02.jpg',409,306)"> --><b>Figure 9.2</b></a> <i>Faster searching method for locating a text string.</i></p>
|
||||
<p><a id="Fig2"><img src="images/09-02.jpg" /><br />
|
||||
<b>Figure 9.2</b></a> <i>Faster searching method for locating a text string.</i></p>
|
||||
|
||||
<p><b>LISTING 9.1 L9-1.ASM</b></p><!-- CODE //-->
|
||||
<p><b>LISTING 9.1 L9-1.ASM</b></p>
|
||||
<pre>
|
||||
; Searches a text buffer for a text string. Uses REPNZ SCASB to sca"n
|
||||
; the buffer for locations that match the first character of the
|
||||
|
|
@ -146,7 +138,7 @@ FindStringDone:
|
|||
ret
|
||||
_FindStringendp
|
||||
end
|
||||
</pre><!-- END CODE //-->
|
||||
</pre>
|
||||
|
||||
<p><br /></p>
|
||||
|
||||
|
|
@ -165,10 +157,6 @@ _FindStringendp
|
|||
|
||||
<div align="center">
|
||||
Graphics Programming Black Book © 2001 Michael Abrash
|
||||
</div><!-- all of the reference materials (books) have the footer and subfoot reveresed -->
|
||||
<!-- reference_subfoot = footer -->
|
||||
<!-- reference_footer = subfoot -->
|
||||
<!-- BEGIN SUB FOOTER -->
|
||||
<!-- END FOOTER -->
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
23
09-04.html
23
09-04.html
|
|
@ -10,16 +10,9 @@
|
|||
<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: Hints My Readers Gave Me</title><!-- HEADER -->
|
||||
<!-- Empty Reference Subhead -->
|
||||
<!--ISBN=1576101746//-->
|
||||
<!--TITLE=Michael Abrash's Graphics Programming Black Book Special Edition//-->
|
||||
<!--AUTHOR=Michael Abrash//-->
|
||||
<!--PUBLISHER=The Coriolis Group, Inc.//-->
|
||||
<title>Michael Abrash's Graphics Programming Black Book Special Edition: Hints My Readers Gave Me</title>
|
||||
<meta name="chapter" content="09" />
|
||||
<meta name="pages" content="178-180" />
|
||||
<!--UNASSIGNED1//-->
|
||||
<!--UNASSIGNED2//-->
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
|
@ -37,7 +30,7 @@
|
|||
|
||||
<p><br /></p>
|
||||
|
||||
<p><b>LISTING 9.2 L9-2.ASM</b></p><!-- CODE //-->
|
||||
<p><b>LISTING 9.2 L9-2.ASM</b></p>
|
||||
<pre>
|
||||
; Searches a text buffer for a text string. Uses REPNZ SCASB to scan
|
||||
; the buffer for locations that match a specified character of the
|
||||
|
|
@ -135,9 +128,9 @@ FindStringDone:
|
|||
ret
|
||||
_FindStringendp
|
||||
end
|
||||
</pre><!-- END CODE //-->
|
||||
</pre>
|
||||
|
||||
<p><b>LISTING 9.3 L9-3.C</b></p><!-- CODE //-->
|
||||
<p><b>LISTING 9.3 L9-3.C</b></p>
|
||||
<pre>
|
||||
/* Program to exercise buffer-search routines in Listings 9.1 & 9.2 */
|
||||
#include <stdio.h>
|
||||
|
|
@ -171,7 +164,7 @@ void main() {
|
|||
strncpy(TempBuffer, MatchPtr, DISPLAY_LENGTH));
|
||||
}
|
||||
}
|
||||
</pre><!-- END CODE //-->
|
||||
</pre>
|
||||
|
||||
<p><br /></p>
|
||||
|
||||
|
|
@ -190,10 +183,6 @@ void main() {
|
|||
|
||||
<div align="center">
|
||||
Graphics Programming Black Book © 2001 Michael Abrash
|
||||
</div><!-- all of the reference materials (books) have the footer and subfoot reveresed -->
|
||||
<!-- reference_subfoot = footer -->
|
||||
<!-- reference_footer = subfoot -->
|
||||
<!-- BEGIN SUB FOOTER -->
|
||||
<!-- END FOOTER -->
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
28
09-05.html
28
09-05.html
|
|
@ -10,16 +10,9 @@
|
|||
<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: Hints My Readers Gave Me</title><!-- HEADER -->
|
||||
<!-- Empty Reference Subhead -->
|
||||
<!--ISBN=1576101746//-->
|
||||
<!--TITLE=Michael Abrash's Graphics Programming Black Book Special Edition//-->
|
||||
<!--AUTHOR=Michael Abrash//-->
|
||||
<!--PUBLISHER=The Coriolis Group, Inc.//-->
|
||||
<title>Michael Abrash's Graphics Programming Black Book Special Edition: Hints My Readers Gave Me</title>
|
||||
<meta name="chapter" content="09" />
|
||||
<meta name="pages" content="180-182" />
|
||||
<!--UNASSIGNED1//-->
|
||||
<!--UNASSIGNED2//-->
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
|
@ -41,11 +34,11 @@
|
|||
|
||||
<p>The point is that you can improve performance dramatically by understanding the nature of the data with which you work. (This is equally true for high-level language programming, by the way.) Listing 9.2 is very similar to and only slightly more complex than Listing 9.1; the difference lies not in elbow grease or cycle counting but in the organic integrating optimizer technology we all carry around in our heads.</p>
|
||||
|
||||
<h4 align="left"><a id="Heading7"></a>Short Sorts</h4>
|
||||
<h4 align="left" id="Heading7">Short Sorts</h4>
|
||||
|
||||
<p>David Stafford (recently of Borland and Borland Japan) who happens to be one of the best assembly language programmers I’ve ever met, has written a C-callable routine that sorts an array of integers in ascending order. That wouldn’t be particularly noteworthy, except that David’s routine, shown in Listing 9.4, is exactly <i>25 bytes</i> long. Look at the code; you’ll keep saying to yourself, “But this doesn’t work...oh, yes, I guess it does.” As they say in the Prego spaghetti sauce ads, <i>it’s in there</i>—and what a job of packing. Anyway, David says that a 24-byte sort routine eludes him, and he’d like to know if anyone can come up with one.</p>
|
||||
|
||||
<p><b>LISTING 9.4 L9-4.ASM</b></p><!-- CODE //-->
|
||||
<p><b>LISTING 9.4 L9-4.ASM</b></p>
|
||||
<pre>
|
||||
.
|
||||
;--------------------------------------------------------------------------
|
||||
|
|
@ -79,9 +72,9 @@ _sort: pop dx ;get return address (entry point)
|
|||
ret
|
||||
|
||||
end
|
||||
</pre><!-- END CODE //-->
|
||||
</pre>
|
||||
|
||||
<h4 align="left"><a id="Heading8"></a>Full 32-Bit Division</h4>
|
||||
<h4 align="left" id="Heading8">Full 32-Bit Division</h4>
|
||||
|
||||
<p>One of the most annoying limitations of the x86 is that while the dividend operand to the <b>DIV</b> instruction can be 32 bits in size, both the divisor and the result must be 16 bits. That’s particularly annoying in regards to the result because sometimes you just don’t know whether the ratio of the dividend to the divisor is greater than 64K-1 or not—and if you guess wrong, you get that godawful Divide By Zero interrupt. So, what is one to do when the result might not fit in 16 bits, or when the dividend is larger than 32 bits? Fall back to a software division approach? That will work—but oh so slowly.</p>
|
||||
|
||||
|
|
@ -89,9 +82,8 @@ _sort: pop dx ;get return address (entry point)
|
|||
|
||||
<p>This technique involves nothing more complicated than breaking up the division into word-sized chunks, starting with the most significant word of the dividend. The most significant word is divided by the divisor (with no chance of overflow because there are only 16 bits in each); then the remainder is prepended to the next 16 bits of dividend, and the process is repeated, as shown in Figure 9.3. This process is equivalent to dividing by hand, except that here we stop to carry the remainder manually only after each word of the dividend; the hardware divide takes care of the rest. Listing 9.5 shows a function to divide an arbitrarily large dividend by a 16-bit divisor, and Listing 9.6 shows a sample division of a large dividend. Note that the same principle can be applied to handling arbitrarily large dividends in 386 native mode code, but in that case the operation can proceed a dword, rather than a word, at a time.</p>
|
||||
|
||||
<p><a id="Fig3"><!-- </A><A HREF="javascript:displayWindow('images/09-03.jpg',406,231 )"> --><img src="images/09-03.jpg" /><br />
|
||||
<!-- </A>
|
||||
<BR><A HREF="javascript:displayWindow('images/09-03.jpg',406,231)"> --><b>Figure 9.3</b></a> <i>Fast multiword division on the 386.</i></p>
|
||||
<p><a id="Fig3"><img src="images/09-03.jpg" /><br />
|
||||
<b>Figure 9.3</b></a> <i>Fast multiword division on the 386.</i></p>
|
||||
|
||||
<p>As for handling signed division with arbitrarily large dividends, that can be done easily enough by remembering the signs of the dividend and divisor, dividing the absolute value of the dividend by the absolute value of the divisor, and applying the stored signs to set the proper signs for the quotient and remainder. There may be more clever ways to produce the same result, by using <b>IDIV</b>, for example; if you know of one, drop me a line c/o Coriolis Group Books.</p>
|
||||
|
||||
|
|
@ -112,10 +104,6 @@ _sort: pop dx ;get return address (entry point)
|
|||
|
||||
<div align="center">
|
||||
Graphics Programming Black Book © 2001 Michael Abrash
|
||||
</div><!-- all of the reference materials (books) have the footer and subfoot reveresed -->
|
||||
<!-- reference_subfoot = footer -->
|
||||
<!-- reference_footer = subfoot -->
|
||||
<!-- BEGIN SUB FOOTER -->
|
||||
<!-- END FOOTER -->
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
25
09-06.html
25
09-06.html
|
|
@ -10,16 +10,9 @@
|
|||
<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: Hints My Readers Gave Me</title><!-- HEADER -->
|
||||
<!-- Empty Reference Subhead -->
|
||||
<!--ISBN=1576101746//-->
|
||||
<!--TITLE=Michael Abrash's Graphics Programming Black Book Special Edition//-->
|
||||
<!--AUTHOR=Michael Abrash//-->
|
||||
<!--PUBLISHER=The Coriolis Group, Inc.//-->
|
||||
<title>Michael Abrash's Graphics Programming Black Book Special Edition: Hints My Readers Gave Me</title>
|
||||
<meta name="chapter" content="09" />
|
||||
<meta name="pages" content="182-185" />
|
||||
<!--UNASSIGNED1//-->
|
||||
<!--UNASSIGNED2//-->
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
|
@ -37,7 +30,7 @@
|
|||
|
||||
<p><br /></p>
|
||||
|
||||
<p><b>LISTING 9.5 L9-5.ASM</b></p><!-- CODE //-->
|
||||
<p><b>LISTING 9.5 L9-5.ASM</b></p>
|
||||
<pre>
|
||||
; Divides an arbitrarily long unsigned dividend by a 16-bit unsigned
|
||||
; divisor. C near-callable as:
|
||||
|
|
@ -105,9 +98,9 @@ DivLoop:
|
|||
ret
|
||||
_Divendp
|
||||
end
|
||||
</pre><!-- END CODE //-->
|
||||
</pre>
|
||||
|
||||
<p><b>LISTING 9.6 L9-6.C</b></p><!-- CODE //-->
|
||||
<p><b>LISTING 9.6 L9-6.C</b></p>
|
||||
<pre>
|
||||
/* Sample use of Div function to perform division when the result
|
||||
doesn’t fit in 16 bits */
|
||||
|
|
@ -125,9 +118,9 @@ main() {
|
|||
k = Div((unsigned int *)&i, sizeof(i), j, (unsigned int *)&m);
|
||||
printf(“%lu / %u = %lu r %u\n”, i, j, m, k);
|
||||
}
|
||||
</pre><!-- END CODE //-->
|
||||
</pre>
|
||||
|
||||
<h4 align="left"><a id="Heading9"></a>Sweet Spot Revisited</h4>
|
||||
<h4 align="left" id="Heading9">Sweet Spot Revisited</h4>
|
||||
|
||||
<p>Way back in Volume 1, Number 1 of <i>PC TECHNIQUES</i>, (April/May 1990) I wrote the very first of that magazine’s HAX (#1), which extolled the virtues of placing your most commonly-used automatic (stack-based) variables within the stack’s “sweet spot,” the area between +127 to -128 bytes away from BP, the stack frame pointer. The reason was that the 8088 can store addressing displacements that fall within that range in a single byte; larger displacements require a full word of storage, increasing code size by a byte per instruction, and thereby slowing down performance due to increased instruction fetching time.</p>
|
||||
|
||||
|
|
@ -162,10 +155,6 @@ main() {
|
|||
|
||||
<div align="center">
|
||||
Graphics Programming Black Book © 2001 Michael Abrash
|
||||
</div><!-- all of the reference materials (books) have the footer and subfoot reveresed -->
|
||||
<!-- reference_subfoot = footer -->
|
||||
<!-- reference_footer = subfoot -->
|
||||
<!-- BEGIN SUB FOOTER -->
|
||||
<!-- END FOOTER -->
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
50
09-07.html
50
09-07.html
|
|
@ -10,16 +10,9 @@
|
|||
<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: Hints My Readers Gave Me</title><!-- HEADER -->
|
||||
<!-- Empty Reference Subhead -->
|
||||
<!--ISBN=1576101746//-->
|
||||
<!--TITLE=Michael Abrash's Graphics Programming Black Book Special Edition//-->
|
||||
<!--AUTHOR=Michael Abrash//-->
|
||||
<!--PUBLISHER=The Coriolis Group, Inc.//-->
|
||||
<title>Michael Abrash's Graphics Programming Black Book Special Edition: Hints My Readers Gave Me</title>
|
||||
<meta name="chapter" content="09" />
|
||||
<meta name="pages" content="185-188" />
|
||||
<!--UNASSIGNED1//-->
|
||||
<!--UNASSIGNED2//-->
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
|
@ -37,30 +30,29 @@
|
|||
|
||||
<p><br /></p>
|
||||
|
||||
<h4 align="left"><a id="Heading10"></a>Hard-Core Cycle Counting</h4>
|
||||
<h4 align="left" id="Heading10">Hard-Core Cycle Counting</h4>
|
||||
|
||||
<p>Next, we come to an item that cycle counters will love, especially since it involves apparently incorrect documentation on Intel’s part. According to Intel’s documents, all <b>RCR</b> and <b>RCL</b> instructions, which perform rotations through the Carry flag, as shown in Figure 9.4, take 9 cycles on the 386 when working with a register operand. My measurements indicate that the 9-cycle execution time almost holds true for <i>multibit</i> rotate-through-carries, which I’ve timed at 8 cycles apiece; for example, <b>RCR AX,CL</b> takes 8 cycles on <i>my</i> 386, as does <b>RCL DX,2</b>. Contrast that with <b>ROR</b> and <b>ROL</b>, which can rotate the contents of a register any number of bits in just 3 cycles.</p>
|
||||
|
||||
<p>However, rotating by one bit through the Carry flag does <i>not</i> take 9 cycles, contrary to Intel’s <i>80386 Programmer’s Reference Manual</i>, or even 8 cycles. In fact, <b>RCR</b> <i>reg</i>,1 and <b>RCL</b> <i>reg</i>,1 take 3 cycles, just like <b>ROR, ROL, SHR,</b> and <b>SHL</b>. At least, that’s how fast they run on my 386, and I very much doubt that you’ll find different execution times on other 386s. (Please let me know if you do, though!)</p>
|
||||
|
||||
<p><a id="Fig4"><!-- </A><A HREF="javascript:displayWindow('images/09-04.jpg',413,249 )"> --><img src="images/09-04.jpg" /><br />
|
||||
<!-- </A>
|
||||
<BR><A HREF="javascript:displayWindow('images/09-04.jpg',413,249)"> --><b>Figure 9.4</b></a> <i>Performing rotate instructions using the Carry flag.</i></p>
|
||||
<p><a id="Fig4"><img src="images/09-04.jpg" /><br />
|
||||
<b>Figure 9.4</b></a> <i>Performing rotate instructions using the Carry flag.</i></p>
|
||||
|
||||
<p>Interestingly, according to Intel’s <i>i486 Microprocessor Programmer’s Reference Manual</i>, the 486 can <b>RCR</b> or <b>RCL</b> a register by one bit in 3 cycles, but takes between 8 and 30 cycles to perform a multibit register <b>RCR</b> or <b>RCL</b>!</p>
|
||||
|
||||
<p>No great lesson here, just a caution to be leery of multibit <b>RCR</b> and <b>RCL</b> when performance matters—and to take cycle-time documentation with a grain of salt.</p>
|
||||
|
||||
<h4 align="left"><a id="Heading11"></a>Hardwired Far Jumps</h4>
|
||||
<h4 align="left" id="Heading11">Hardwired Far Jumps</h4>
|
||||
|
||||
<p>Did you ever wonder how to code a far jump to an absolute address in assembly language? Probably not, but if you ever do, you’re going to be glad for this next item, because the obvious solution doesn’t work. You might think all it would take to jump to, say, 1000:5 would be <b>JMP FAR PTR 1000:5</b>, but you’d be wrong. That won’t even assemble. You might then think to construct in memory a far pointer containing 1000:5, as in the following:</p><!-- CODE SNIP //-->
|
||||
<p>Did you ever wonder how to code a far jump to an absolute address in assembly language? Probably not, but if you ever do, you’re going to be glad for this next item, because the obvious solution doesn’t work. You might think all it would take to jump to, say, 1000:5 would be <b>JMP FAR PTR 1000:5</b>, but you’d be wrong. That won’t even assemble. You might then think to construct in memory a far pointer containing 1000:5, as in the following:</p>
|
||||
<pre>
|
||||
Ptr dd ?
|
||||
:
|
||||
mov word ptr [Ptr],5
|
||||
mov word ptr [Ptr+2],1000h
|
||||
jmp [Ptr]
|
||||
</pre><!-- END CODE SNIP //-->
|
||||
</pre>
|
||||
|
||||
<p>That will work, but at a price in performance. On an 8088, <b>JMP DWORD PTR [<i>mem</i>]</b> (an indirect far jump) takes at least 37 cycles; <b>JMP DWORD PTR <i>label</i></b> (a direct far jump) takes only 15 cycles (plus, almost certainly, some cycles for instruction fetching). On a 386, an indirect far jump is documented to take at least 43 cycles in real mode (31 in protected mode); a direct far jump is documented to take at least 12 cycles, about three times faster. In truth, the difference between those two is nowhere near that big; the fastest I’ve measured for a direct far jump is 21 cycles, and I’ve measured indirect far jumps as fast as 30 cycles, so direct is still faster, but not by so much. (Oh, those cycle-time documentation blues!) Also, a direct far jump is documented to take at least 27 cycles in protected mode; why the big difference in protected mode, I have no idea.</p>
|
||||
|
||||
|
|
@ -68,7 +60,7 @@ Ptr dd ?
|
|||
|
||||
<p>Listing 9.7 shows a short program that performs a direct far call to 1000:5. (Don’t run it, unless you want to crash your system!) It does this by creating a dummy segment at 1000H, so that the label <b>FarLabel</b> can be created with the desired far attribute at the proper location. (Segments created with “AT” don’t cause the generation of any actual bytes or the allocation of any memory; they’re just templates.) It’s a little kludgey, but at least it does work. There may be a better solution; if you have one, pass it along.</p>
|
||||
|
||||
<p><b>LISTING 9.7 L9-7.ASM</b></p><!-- CODE //-->
|
||||
<p><b>LISTING 9.7 L9-7.ASM</b></p>
|
||||
<pre>
|
||||
; Program to perform a direct far jump to address 1000:5.
|
||||
; *** Do not run this program! It’s just an example of how ***
|
||||
|
|
@ -86,34 +78,34 @@ FarSeg ends
|
|||
start:
|
||||
jmp FarLabel
|
||||
end start
|
||||
</pre><!-- END CODE //-->
|
||||
</pre>
|
||||
|
||||
<p>By the way, if you’re wondering how I figured this out, I merely applied my good friend Dan Illowsky’s long-standing rule for dealing with MASM:</p>
|
||||
|
||||
<p>If the obvious doesn’t work (and it usually doesn’t), just try everything you can think of, no matter how ridiculous, until you find something that does—a rule with plenty of history on its side.</p>
|
||||
|
||||
<h4 align="left"><a id="Heading12"></a>Setting 32-Bit Registers: Time versus Space</h4>
|
||||
<h4 align="left" id="Heading12">Setting 32-Bit Registers: Time versus Space</h4>
|
||||
|
||||
<p>To finish up this chapter, consider these two items. First, in 32-bit protected mode,</p><!-- CODE SNIP //-->
|
||||
<p>To finish up this chapter, consider these two items. First, in 32-bit protected mode,</p>
|
||||
<pre>
|
||||
sub eax,eax
|
||||
inc eax
|
||||
</pre><!-- END CODE SNIP //-->
|
||||
</pre>
|
||||
|
||||
<p>takes 4 cycles to execute, but is only 3 bytes long, while</p><!-- CODE SNIP //-->
|
||||
<p>takes 4 cycles to execute, but is only 3 bytes long, while</p>
|
||||
<pre>
|
||||
mov eax,1
|
||||
</pre><!-- END CODE SNIP //-->
|
||||
</pre>
|
||||
|
||||
<p>takes only 2 cycles to execute, but is 5 bytes long (because native mode constants are dwords and the <b>MOV</b> instruction doesn’t sign-extend). Both code fragments are ways to set <b>EAX</b> to 1 (although the first affects the flags and the second doesn’t); this is a classic trade-off of speed for space. Second,</p><!-- CODE SNIP //-->
|
||||
<p>takes only 2 cycles to execute, but is 5 bytes long (because native mode constants are dwords and the <b>MOV</b> instruction doesn’t sign-extend). Both code fragments are ways to set <b>EAX</b> to 1 (although the first affects the flags and the second doesn’t); this is a classic trade-off of speed for space. Second,</p>
|
||||
<pre>
|
||||
or ebx,-1
|
||||
</pre><!-- END CODE SNIP //-->
|
||||
</pre>
|
||||
|
||||
<p>takes 2 cycles to execute and is 3 bytes long, while</p><!-- CODE SNIP //-->
|
||||
<p>takes 2 cycles to execute and is 3 bytes long, while</p>
|
||||
<pre>
|
||||
move bx,-1
|
||||
</pre><!-- END CODE SNIP //-->
|
||||
</pre>
|
||||
|
||||
<p>takes 2 cycles to execute and is 5 bytes long. Both instructions set <b>EBX</b> to -1; this is a classic trade-off of—gee, it’s not a trade-off at all, is it? <b>OR</b> is a better way to set a 32-bit register to all 1-bits, just as <b>SUB</b> or <b>XOR</b> is a better way to set a register to all 0-bits. Who woulda thunk it? Just goes to show how the 32-bit displacements and constants of 386 native mode change the familiar landscape of 80x86 optimization.</p>
|
||||
|
||||
|
|
@ -136,10 +128,6 @@ move bx,-1
|
|||
|
||||
<div align="center">
|
||||
Graphics Programming Black Book © 2001 Michael Abrash
|
||||
</div><!-- all of the reference materials (books) have the footer and subfoot reveresed -->
|
||||
<!-- reference_subfoot = footer -->
|
||||
<!-- reference_footer = subfoot -->
|
||||
<!-- BEGIN SUB FOOTER -->
|
||||
<!-- END FOOTER -->
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
23
10-01.html
23
10-01.html
|
|
@ -10,16 +10,9 @@
|
|||
<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: Patient Coding, Faster Code</title><!-- HEADER -->
|
||||
<!-- Empty Reference Subhead -->
|
||||
<!--ISBN=1576101746//-->
|
||||
<!--TITLE=Michael Abrash's Graphics Programming Black Book Special Edition//-->
|
||||
<!--AUTHOR=Michael Abrash//-->
|
||||
<!--PUBLISHER=The Coriolis Group, Inc.//-->
|
||||
<title>Michael Abrash's Graphics Programming Black Book Special Edition: Patient Coding, Faster Code</title>
|
||||
<meta name="chapter" content="10" />
|
||||
<meta name="pages" content="190-193" />
|
||||
<!--UNASSIGNED1//-->
|
||||
<!--UNASSIGNED2//-->
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
|
@ -37,10 +30,10 @@
|
|||
|
||||
<p><br /></p>
|
||||
|
||||
<h2><a id="Heading1"></a>Chapter 10<br />
|
||||
<h2 id="Heading1">Chapter 10<br />
|
||||
Patient Coding, Faster Code</h2>
|
||||
|
||||
<h3><a id="Heading2"></a>How Working Quickly Can Bring Execution to a Crawl</h3>
|
||||
<h3 id="Heading2">How Working Quickly Can Bring Execution to a Crawl</h3>
|
||||
|
||||
<p>My grandfather does <i>The New York Times</i> crossword puzzle every Sunday. In ink. With nary a blemish.</p>
|
||||
|
||||
|
|
@ -64,7 +57,7 @@
|
|||
|
||||
<p>In this chapter, I’m going to walk you through a simple but illustrative case history that nicely points up the wisdom of delaying gratification when faced with programming problems, so that your mind has time to chew on the problems from other angles. The alternative solutions you find by doing this may seem obvious, once you’ve come up with them. They may not even differ greatly from your initial solutions. Often, however, they will be much better—and you’ll never even have the chance to decide whether they’re better or not if you take the first thing that comes into your head and run with it.</p>
|
||||
|
||||
<h4 align="left"><a id="Heading3"></a>The Case for Delayed Gratification</h4>
|
||||
<h4 align="left" id="Heading3">The Case for Delayed Gratification</h4>
|
||||
|
||||
<p>Once upon a time, I set out to read <i>Algorithms</i>, by Robert Sedgewick (Addison-Wesley), which turned out to be a wonderful, stimulating, and most useful book, one that I recommend highly. My story, however, involves only what happened in the first 12 pages, for it was in those pages that Sedgewick discussed Euclid’s algorithm.</p>
|
||||
|
||||
|
|
@ -72,7 +65,7 @@
|
|||
|
||||
<p>The problem at hand, then, is simply this: Find the largest integer value that evenly divides two arbitrary positive integers. That’s all there is to it. So warm up your pattern matchers...and go!</p>
|
||||
|
||||
<h3><a id="Heading4"></a>The Brute-Force Syndrome</h3>
|
||||
<h3 id="Heading4">The Brute-Force Syndrome</h3>
|
||||
|
||||
<p>I have a funny feeling that you’d already figured out how to find the GCD before I even said “go.” That’s what I did when reading <i>Algorithms;</i> before I read another word, I had to figure it out for myself. Programmers are like that; give them a problem and their eyes immediately glaze over as they try to solve it before you’ve even shut your mouth. That sort of instant response can certainly be impressive, but it can backfire, too, as it did in my case.</p>
|
||||
|
||||
|
|
@ -95,10 +88,6 @@
|
|||
|
||||
<div align="center">
|
||||
Graphics Programming Black Book © 2001 Michael Abrash
|
||||
</div><!-- all of the reference materials (books) have the footer and subfoot reveresed -->
|
||||
<!-- reference_subfoot = footer -->
|
||||
<!-- reference_footer = subfoot -->
|
||||
<!-- BEGIN SUB FOOTER -->
|
||||
<!-- END FOOTER -->
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
30
10-02.html
30
10-02.html
|
|
@ -10,16 +10,9 @@
|
|||
<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: Patient Coding, Faster Code</title><!-- HEADER -->
|
||||
<!-- Empty Reference Subhead -->
|
||||
<!--ISBN=1576101746//-->
|
||||
<!--TITLE=Michael Abrash's Graphics Programming Black Book Special Edition//-->
|
||||
<!--AUTHOR=Michael Abrash//-->
|
||||
<!--PUBLISHER=The Coriolis Group, Inc.//-->
|
||||
<title>Michael Abrash's Graphics Programming Black Book Special Edition: Patient Coding, Faster Code</title>
|
||||
<meta name="chapter" content="10" />
|
||||
<meta name="pages" content="193-196" />
|
||||
<!--UNASSIGNED1//-->
|
||||
<!--UNASSIGNED2//-->
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
|
@ -43,9 +36,8 @@
|
|||
|
||||
<p>Listing 10.1 is an implementation of the brute-force approach to GCD calculation. Table 10.1 shows how long it takes this approach to find the GCD for several integer pairs. As expected, performance is extremely poor when iS is large.</p>
|
||||
|
||||
<p><a id="Fig1"><!-- </A><A HREF="javascript:displayWindow('images/10-01.jpg',411,233 )"> --><img src="images/10-01.jpg" /><br />
|
||||
<!-- </A>
|
||||
<BR><A HREF="javascript:displayWindow('images/10-01.jpg',411,233)"> --><b>Figure 10.1</b></a> <i>Using a brute-force algorithm to find a GCD.</i></p>
|
||||
<p><a id="Fig1"><img src="images/10-01.jpg" /><br />
|
||||
<b>Figure 10.1</b></a> <i>Using a brute-force algorithm to find a GCD.</i></p>
|
||||
|
||||
<table width="100%">
|
||||
<tr>
|
||||
|
|
@ -222,7 +214,7 @@
|
|||
</tr>
|
||||
</table>
|
||||
|
||||
<p><b>LISTING 10.1 L10-1.C</b></p><!-- CODE //-->
|
||||
<p><b>LISTING 10.1 L10-1.C</b></p>
|
||||
<pre>
|
||||
/* Finds and returns the greatest common divisor of two positive
|
||||
integers. Works by trying every integral divisor between the
|
||||
|
|
@ -246,13 +238,13 @@ unsigned int gcd(unsigned int int1, unsigned int int2) {
|
|||
;
|
||||
return(trial_divisor);
|
||||
}
|
||||
</pre><!-- END CODE //-->
|
||||
</pre>
|
||||
|
||||
<h4 align="left"><a id="Heading5"></a>Wasted Breakthroughs</h4>
|
||||
<h4 align="left" id="Heading5">Wasted Breakthroughs</h4>
|
||||
|
||||
<p>Sedgewick’s first solution to the GCD problem was pretty much the one I came up with. He then pointed out that the GCD of iL and iS is the same as the GCD of iL-iS and iS. This was obvious (once Sedgewick pointed it out); by the very nature of division, any number that divides iL evenly nL times and iS evenly nS times must divide iL-iS evenly nL-nS times. Given that insight, I immediately designed a new, faster approach, shown in Listing 10.2.</p>
|
||||
|
||||
<p><b>LISTING 10.2 L10-2.C</b></p><!-- CODE //-->
|
||||
<p><b>LISTING 10.2 L10-2.C</b></p>
|
||||
<pre>
|
||||
/* Finds and returns the greatest common divisor of two positive
|
||||
integers. Works by subtracting the smaller integer from the
|
||||
|
|
@ -283,7 +275,7 @@ unsigned int gcd(unsigned int int1, unsigned int int2) {
|
|||
/* Now recursively call this function to continue the process */
|
||||
return(gcd(int1, int2));
|
||||
}
|
||||
</pre><!-- END CODE //-->
|
||||
</pre>
|
||||
|
||||
<p><br /></p>
|
||||
|
||||
|
|
@ -302,10 +294,6 @@ unsigned int gcd(unsigned int int1, unsigned int int2) {
|
|||
|
||||
<div align="center">
|
||||
Graphics Programming Black Book © 2001 Michael Abrash
|
||||
</div><!-- all of the reference materials (books) have the footer and subfoot reveresed -->
|
||||
<!-- reference_subfoot = footer -->
|
||||
<!-- reference_footer = subfoot -->
|
||||
<!-- BEGIN SUB FOOTER -->
|
||||
<!-- END FOOTER -->
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
37
10-03.html
37
10-03.html
|
|
@ -10,16 +10,9 @@
|
|||
<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: Patient Coding, Faster Code</title><!-- HEADER -->
|
||||
<!-- Empty Reference Subhead -->
|
||||
<!--ISBN=1576101746//-->
|
||||
<!--TITLE=Michael Abrash's Graphics Programming Black Book Special Edition//-->
|
||||
<!--AUTHOR=Michael Abrash//-->
|
||||
<!--PUBLISHER=The Coriolis Group, Inc.//-->
|
||||
<title>Michael Abrash's Graphics Programming Black Book Special Edition: Patient Coding, Faster Code</title>
|
||||
<meta name="chapter" content="10" />
|
||||
<meta name="pages" content="196-200" />
|
||||
<!--UNASSIGNED1//-->
|
||||
<!--UNASSIGNED2//-->
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
|
@ -39,15 +32,14 @@
|
|||
|
||||
<p>Listing 10.2 repeatedly subtracts iS from iL until iL becomes less than or equal to iS. If iL becomes equal to iS, then that’s the GCD; alternatively, if iL becomes <i>less</i> than iS, iL and iS switch values, and the process is repeated, as shown in Figure 10.2. The number of iterations this approach requires relative to Listing 10.1 depends heavily on the values of iL and iS, so it’s not always faster, but, as Table 10.1 indicates, Listing 10.2 is generally much better code.</p>
|
||||
|
||||
<p><a id="Fig2"><!-- </A><A HREF="javascript:displayWindow('images/10-02.jpg',415,306 )"> --><img src="images/10-02.jpg" /><br />
|
||||
<!-- </A>
|
||||
<BR><A HREF="javascript:displayWindow('images/10-02.jpg',415,306)"> --><b>Figure 10.2</b></a> <i>Using repeated subtraction algorithm to find a GCD.</i></p>
|
||||
<p><a id="Fig2"><img src="images/10-02.jpg" /><br />
|
||||
<b>Figure 10.2</b></a> <i>Using repeated subtraction algorithm to find a GCD.</i></p>
|
||||
|
||||
<p>Listing 10.2 is a far graver misstep than Listing 10.1, for all that it’s faster. Listing 10.1 is obviously a hacked-up, brute-force approach; no one could mistake it for anything else. It could be speeded up in any of a number of ways with a little thought. (Simply skipping testing all the divisors between iS and iS/2, not inclusive, would cut the worst-case time in half, for example; that’s not a particularly <i>good</i> optimization, but it illustrates how easily Listing 10.1 can be improved.) Listing 10.1 is a hack job, crying out for inspiration.</p>
|
||||
|
||||
<p>Listing 10.2, on the other hand, has gotten the inspiration—and largely wasted it through haste. Had Sedgewick not told me otherwise, I might well have assumed that Listing 10.2 was optimized, a mistake I would never have made with Listing 10.1. I experienced a conceptual breakthrough when I understood Sedgewick’s point: A smaller number can be subtracted from a larger number without affecting their GCD, thereby inexpensively reducing the scale of the problem. And, in my hurry to make this breakthrough reality, I missed its full scope. As Sedgewick says on the very next page, the number that one gets by subtracting iS from iL until iL is less than iS is precisely the same as the remainder that one gets by dividing iL by iS—again, this is inherent in the nature of division—and <i>that</i> is the basis for Euclid’s algorithm, shown in Figure 10.3. Listing 10.3 is an implementation of Euclid’s algorithm.</p>
|
||||
|
||||
<p><b>LISTING 10.3 L10-3.C</b></p><!-- CODE //-->
|
||||
<p><b>LISTING 10.3 L10-3.C</b></p>
|
||||
<pre>
|
||||
/* Finds and returns the greatest common divisor of two integers.
|
||||
Uses Euclid’s algorithm: divides the larger integer by the
|
||||
|
|
@ -92,7 +84,7 @@ static unsigned int gcd_recurs(unsigned int larger_int,
|
|||
continue the process */
|
||||
return(gcd_recurs(smaller_int, temp));
|
||||
}
|
||||
</pre><!-- END CODE //-->
|
||||
</pre>
|
||||
|
||||
<p>As you can see from Table 10.1, Euclid’s algorithm is superior, especially for large numbers (and imagine if we were working with large <i>longs!</i>).</p>
|
||||
|
||||
|
|
@ -104,17 +96,16 @@ static unsigned int gcd_recurs(unsigned int larger_int,
|
|||
</tr>
|
||||
</table>
|
||||
|
||||
<p><a id="Fig3"><!-- </A><A HREF="javascript:displayWindow('images/10-03.jpg',411,279 )"> --><img src="images/10-03.jpg" /><br />
|
||||
<!-- </A>
|
||||
<BR><A HREF="javascript:displayWindow('images/10-03.jpg',411,279)"> --><b>Figure 10.3</b></a> <i>Using Euclid’s algorithm to find a GCD.</i></p>
|
||||
<p><a id="Fig3"><img src="images/10-03.jpg" /><br />
|
||||
<b>Figure 10.3</b></a> <i>Using Euclid’s algorithm to find a GCD.</i></p>
|
||||
|
||||
<p>Give your mind time and space to wander around the edges of important programming problems before you settle on any one approach. I titled this book’s first chapter “The Best Optimizer Is between Your Ears,” and that’s still true; what’s even more true is that the optimizer between your ears does its best work not at the implementation stage, but at the very beginning, when you try to imagine how what you want to do and what a computer is capable of doing can best be brought together.</p>
|
||||
|
||||
<h3><a id="Heading6"></a>Recursion</h3>
|
||||
<h3 id="Heading6">Recursion</h3>
|
||||
|
||||
<p>Euclid’s algorithm lends itself to recursion beautifully, so much so that an implementation like Listing 10.3 comes almost without thought. Again, though, take a moment to stop and consider what’s really going on, at the assembly language level, in Listing 10.3. There’s recursion and then there’s recursion; code recursion and data recursion, to be exact. Listing 10.3 is code recursion—recursion through calls—the sort most often used because it is conceptually simplest. However, code recursion tends to be slow because it pushes parameters and calls a subroutine for every iteration. Listing 10.4, which uses data recursion, is much faster and no more complicated than Listing 10.3. Actually, you could just say that Listing 10.4 uses a loop and ignore any mention of recursion; conceptually, though, Listing 10.4 performs the same recursive operations that Listing 10.3 does.</p>
|
||||
|
||||
<p><b>LISTING 10.4 L10-4.C</b></p><!-- CODE //-->
|
||||
<p><b>LISTING 10.4 L10-4.C</b></p>
|
||||
<pre>
|
||||
/* Finds and returns the greatest common divisor of two integers.
|
||||
Uses Euclid’s algorithm: divides the larger integer by the
|
||||
|
|
@ -148,9 +139,9 @@ unsigned int gcd(unsigned int int1, unsigned int int2) {
|
|||
int2 = temp;
|
||||
}
|
||||
}
|
||||
</pre><!-- END CODE //-->
|
||||
</pre>
|
||||
|
||||
<h4 align="left"><a id="Heading7"></a>Patient Optimization</h4>
|
||||
<h4 align="left" id="Heading7">Patient Optimization</h4>
|
||||
|
||||
<p>At long last, we’re ready to optimize GCD determination in the classic sense. Table 10.1 shows the performance of Listing 10.4 with and without Microsoft C/C<small>++</small>’s maximum optimization, and also shows the performance of Listing 10.5, an assembly language version of Listing 10.4. Sure, the optimized versions are faster than the unoptimized version of Listing 10.4—but the gains are small compared to those realized from the higher-level optimizations in Listings 10.2 through 10.4.</p>
|
||||
|
||||
|
|
@ -171,10 +162,6 @@ unsigned int gcd(unsigned int int1, unsigned int int2) {
|
|||
|
||||
<div align="center">
|
||||
Graphics Programming Black Book © 2001 Michael Abrash
|
||||
</div><!-- all of the reference materials (books) have the footer and subfoot reveresed -->
|
||||
<!-- reference_subfoot = footer -->
|
||||
<!-- reference_footer = subfoot -->
|
||||
<!-- BEGIN SUB FOOTER -->
|
||||
<!-- END FOOTER -->
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
19
10-04.html
19
10-04.html
|
|
@ -10,16 +10,9 @@
|
|||
<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: Patient Coding, Faster Code</title><!-- HEADER -->
|
||||
<!-- Empty Reference Subhead -->
|
||||
<!--ISBN=1576101746//-->
|
||||
<!--TITLE=Michael Abrash's Graphics Programming Black Book Special Edition//-->
|
||||
<!--AUTHOR=Michael Abrash//-->
|
||||
<!--PUBLISHER=The Coriolis Group, Inc.//-->
|
||||
<title>Michael Abrash's Graphics Programming Black Book Special Edition: Patient Coding, Faster Code</title>
|
||||
<meta name="chapter" content="10" />
|
||||
<meta name="pages" content="200-203" />
|
||||
<!--UNASSIGNED1//-->
|
||||
<!--UNASSIGNED2//-->
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
|
@ -37,7 +30,7 @@
|
|||
|
||||
<p><br /></p>
|
||||
|
||||
<p><b>LISTING 10.5 L10-5.ASM</b></p><!-- CODE //-->
|
||||
<p><b>LISTING 10.5 L10-5.ASM</b></p>
|
||||
<pre>
|
||||
; Finds and returns the greatest common divisor of two integers.
|
||||
; Uses Euclid’s algorithm: divides the larger integer by the
|
||||
|
|
@ -126,7 +119,7 @@ Done:
|
|||
ret
|
||||
_gcd endp
|
||||
end
|
||||
</pre><!-- END CODE //-->
|
||||
</pre>
|
||||
|
||||
<p>Assembly language optimization is pattern matching on a local scale. Frankly, it’s also the sort of boring, brute-force work that people are lousy at; compilers could out-optimize you at this level with one pass tied behind their back <i>if</i> they knew as much about the code you’re writing as you do, which they don’t.</p>
|
||||
|
||||
|
|
@ -163,10 +156,6 @@ _gcd endp
|
|||
|
||||
<div align="center">
|
||||
Graphics Programming Black Book © 2001 Michael Abrash
|
||||
</div><!-- all of the reference materials (books) have the footer and subfoot reveresed -->
|
||||
<!-- reference_subfoot = footer -->
|
||||
<!-- reference_footer = subfoot -->
|
||||
<!-- BEGIN SUB FOOTER -->
|
||||
<!-- END FOOTER -->
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
33
11-01.html
33
11-01.html
|
|
@ -10,16 +10,9 @@
|
|||
<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: Pushing the 286 and 386</title><!-- HEADER -->
|
||||
<!-- Empty Reference Subhead -->
|
||||
<!--ISBN=1576101746//-->
|
||||
<!--TITLE=Michael Abrash's Graphics Programming Black Book Special Edition//-->
|
||||
<!--AUTHOR=Michael Abrash//-->
|
||||
<!--PUBLISHER=The Coriolis Group, Inc.//-->
|
||||
<title>Michael Abrash's Graphics Programming Black Book Special Edition: Pushing the 286 and 386</title>
|
||||
<meta name="chapter" content="11" />
|
||||
<meta name="pages" content="205-210" />
|
||||
<!--UNASSIGNED1//-->
|
||||
<!--UNASSIGNED2//-->
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
|
@ -37,16 +30,16 @@
|
|||
|
||||
<p><br /></p>
|
||||
|
||||
<h2><a id="Heading1"></a>Chapter 11<br />
|
||||
<h2 id="Heading1">Chapter 11<br />
|
||||
Pushing the 286 and 386</h2>
|
||||
|
||||
<h3><a id="Heading2"></a>New Registers, New Instructions, New Timings, New Complications</h3>
|
||||
<h3 id="Heading2">New Registers, New Instructions, New Timings, New Complications</h3>
|
||||
|
||||
<p>This chapter, adapted from my earlier book <i>Zen of Assembly Language</i> (1989; now out of print), provides an overview of the 286 and 386, often contrasting those processors with the 8088. At the time I originally wrote this, the 8088 was the king of processors, and the 286 and 386 were the new kids on the block. Today, of course, all three processors are past their primes, but many millions of each are still in use, and the 386 in particular is still well worth considering when optimizing software.</p>
|
||||
|
||||
<p>This chapter provides an interesting look at the evolution of the x86 architecture, to a greater degree than you might expect, for the x86 family came into full maturity with the 386; the 486 and the Pentium are really nothing more than faster 386s, with very little in the way of new functionality. In contrast, the 286 added a number of instructions, respectable performance, and protected mode to the 8088’s capabilities, and the 386 added more instructions and a whole new set of addressing modes, and brought the x86 family into the 32-bit world that represents the future (and, increasingly, the present) of personal computing. This chapter also provides insight into the effects on optimization of the variations in processors and memory architectures that are common in the PC world. So, although the 286 and 386 no longer represent the mainstream of computing, this chapter is a useful mix of history lesson, x86 overview, and details on two workhorse processors that are still in wide use.</p>
|
||||
|
||||
<h4 align="left"><a id="Heading3"></a>Family Matters</h4>
|
||||
<h4 align="left" id="Heading3">Family Matters</h4>
|
||||
|
||||
<p>While the x86 family is a large one, only a few members of the family—which includes the 8088, 8086, 80188, 80186, 286, 386SX, 386DX, numerous permutations of the 486, and now the Pentium—really matter.</p>
|
||||
|
||||
|
|
@ -60,21 +53,21 @@
|
|||
|
||||
<p>This leaves us with just two processors: the 286 and the 386. Each was <i>the</i> PC standard in its day. The 286 is no longer used in new systems, but there are millions of 286-based systems still in daily use. The 386 is still being used in new systems, although it’s on the downhill leg of its lifespan, and it is in even wider use than the 286. The future clearly belongs to the 486 and Pentium, but the 286 and 386 are still very much a part of the present-day landscape.</p>
|
||||
|
||||
<h4 align="left"><a id="Heading4"></a>Crossing the Gulf to the 286 and the 386</h4>
|
||||
<h4 align="left" id="Heading4">Crossing the Gulf to the 286 and the 386</h4>
|
||||
|
||||
<p>Apart from vastly improved performance, the biggest difference between the 8088 and the 286 and 386 (as well as the later Intel CPUs) is that the 286 introduced protected mode, and the 386 greatly expanded the capabilities of protected mode. We’re only going to talk about real-mode operation of the 286 and 386 in this book, however. Protected mode offers a whole new memory management scheme, one that isn’t supported by the 8088. Only code specifically written for protected mode can run in that mode; it’s an alien and hostile environment for MS-DOS programs.</p>
|
||||
|
||||
<p>In particular, segments are different creatures in protected mode. They’re <i>selectors</i>—indexes into a table of segment descriptors—rather than plain old registers, and can’t be set to arbitrary values. That means that segments can’t be used for temporary storage or as part of a fast indivisible 32-bit load from memory, as in</p><!-- CODE SNIP //-->
|
||||
<p>In particular, segments are different creatures in protected mode. They’re <i>selectors</i>—indexes into a table of segment descriptors—rather than plain old registers, and can’t be set to arbitrary values. That means that segments can’t be used for temporary storage or as part of a fast indivisible 32-bit load from memory, as in</p>
|
||||
<pre>
|
||||
les ax,dword ptr [LongVar]
|
||||
mov dx,es
|
||||
</pre><!-- END CODE SNIP //-->
|
||||
</pre>
|
||||
|
||||
<p>which loads <b>LongVar</b> into DX:AX faster than this:</p><!-- CODE SNIP //-->
|
||||
<p>which loads <b>LongVar</b> into DX:AX faster than this:</p>
|
||||
<pre>
|
||||
mov ax,word ptr [LongVar]
|
||||
mov dx,word ptr [LongVar+2]
|
||||
</pre><!-- END CODE SNIP //-->
|
||||
</pre>
|
||||
|
||||
<p>Protected mode uses those altered segment registers to offer access to a great deal more memory than real mode: The 286 supports 16 megabytes of memory, while the 386 supports 4 gigabytes (4K megabytes) of physical memory and 64 <i>terabytes</i> (64K gigabytes!) of virtual memory.</p>
|
||||
|
||||
|
|
@ -82,7 +75,7 @@ mov dx,word ptr [LongVar+2]
|
|||
|
||||
<p>In short, taken as a whole, protected mode programming is a different kettle of fish altogether from what I’ve been describing in this book. There’s certainly a knack to optimizing specifically for protected mode under a given operating system...but it’s not what we’ve been learning, and now is not the time to pursue it further. In general, though, the optimization strategies discussed in this book still hold true in protected mode; it’s just issues specific to protected mode or a particular operating system that we won’t discuss.</p>
|
||||
|
||||
<h4 align="left"><a id="Heading5"></a>In the Lair of the Cycle-Eaters, Part II</h4>
|
||||
<h4 align="left" id="Heading5">In the Lair of the Cycle-Eaters, Part II</h4>
|
||||
|
||||
<p>Under the programming interface, the 286 and 386 differ considerably from the 8088. Nonetheless, with one exception and one addition, the cycle-eaters remain much the same on computers built around the 286 and 386. Next, we’ll review each of the familiar cycle-eaters I covered in Chapter 4 as they apply to the 286 and 386, and we’ll look at the new member of the gang, the data alignment cycle-eater.</p>
|
||||
|
||||
|
|
@ -105,10 +98,6 @@ mov dx,word ptr [LongVar+2]
|
|||
|
||||
<div align="center">
|
||||
Graphics Programming Black Book © 2001 Michael Abrash
|
||||
</div><!-- all of the reference materials (books) have the footer and subfoot reveresed -->
|
||||
<!-- reference_subfoot = footer -->
|
||||
<!-- reference_footer = subfoot -->
|
||||
<!-- BEGIN SUB FOOTER -->
|
||||
<!-- END FOOTER -->
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
21
11-02.html
21
11-02.html
|
|
@ -10,16 +10,9 @@
|
|||
<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: Pushing the 286 and 386</title><!-- HEADER -->
|
||||
<!-- Empty Reference Subhead -->
|
||||
<!--ISBN=1576101746//-->
|
||||
<!--TITLE=Michael Abrash's Graphics Programming Black Book Special Edition//-->
|
||||
<!--AUTHOR=Michael Abrash//-->
|
||||
<!--PUBLISHER=The Coriolis Group, Inc.//-->
|
||||
<title>Michael Abrash's Graphics Programming Black Book Special Edition: Pushing the 286 and 386</title>
|
||||
<meta name="chapter" content="11" />
|
||||
<meta name="pages" content="210-212" />
|
||||
<!--UNASSIGNED1//-->
|
||||
<!--UNASSIGNED2//-->
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
|
@ -47,7 +40,7 @@
|
|||
|
||||
<p>The most significant reason that the prefetch queue cycle-eater not only survives but prospers on the 286 and 386, however, lies in the various memory architectures used in computers built around the 286 and 386. Due to the memory architectures, the 8-bit bus cycle-eater is replaced by a new form of the wait state cycle-eater: wait states on accesses to normal system memory.</p>
|
||||
|
||||
<h4 align="center"><a id="Heading6"></a>System Wait States</h4>
|
||||
<h4 align="center" id="Heading6">System Wait States</h4>
|
||||
|
||||
<p>The 286 and 386 were designed to lose relatively little performance to the prefetch queue cycle-eater...<i>when used with zero-wait-state memory:</i> memory that can complete memory accesses so rapidly that no wait states are needed. However, true zero-wait-state memory is almost never used with those processors. Why? Because memory that can keep up with a 286 is fairly expensive, and memory that can keep up with a 386 is <i>very</i> expensive. Instead, computer designers use alternative memory architectures that offer more performance for the dollar—but less performance overall—than zero-wait-state memory. (It <i>is</i> possible to build zero-wait-state systems for the 286 and 386; it’s just so expensive that it’s rarely done.)</p>
|
||||
|
||||
|
|
@ -71,7 +64,7 @@
|
|||
|
||||
<p>Let’s check out the prefetch queue cycle-eater in action. Listing 11.1 times <b>MOV [WordVar],0</b>. The Zen timer reports that on a one-wait-state 10 MHz 286-based AT clone (the computer used for all tests in this chapter), Listing 11.1 runs in 1.27 µs per instruction. That’s 12.7 cycles per instruction, just as we calculated. (That extra seven-tenths of a cycle comes from DRAM refresh, which we’ll get to shortly.)</p>
|
||||
|
||||
<p><b>LISTING 11.1 L11-1.ASM</b></p><!-- CODE //-->
|
||||
<p><b>LISTING 11.1 L11-1.ASM</b></p>
|
||||
<pre>
|
||||
;
|
||||
; *** Listing 11.1 ***
|
||||
|
|
@ -92,7 +85,7 @@ Skip:
|
|||
mov [WordVar],0
|
||||
endm
|
||||
call ZTimerOff
|
||||
</pre><!-- END CODE //-->
|
||||
</pre>
|
||||
|
||||
<p>What does this mean? It means that, practically speaking, the 286 as used in the AT doesn’t have a 16-bit bus. From a performance perspective, the 286 in an AT has two-thirds of a 16-bit bus (a 10.7-bit bus?), since every bus access on an AT takes 50 percent longer than it should. A 286 running at 10 MHz <i>should</i> be able to access memory at a maximum rate of 1 word every 200 ns; in a 10 MHz AT, however, that rate is reduced to 1 word every 300 ns by the one-wait-state memory.</p>
|
||||
|
||||
|
|
@ -113,10 +106,6 @@ Skip:
|
|||
|
||||
<div align="center">
|
||||
Graphics Programming Black Book © 2001 Michael Abrash
|
||||
</div><!-- all of the reference materials (books) have the footer and subfoot reveresed -->
|
||||
<!-- reference_subfoot = footer -->
|
||||
<!-- reference_footer = subfoot -->
|
||||
<!-- BEGIN SUB FOOTER -->
|
||||
<!-- END FOOTER -->
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
32
11-03.html
32
11-03.html
|
|
@ -10,16 +10,9 @@
|
|||
<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: Pushing the 286 and 386</title><!-- HEADER -->
|
||||
<!-- Empty Reference Subhead -->
|
||||
<!--ISBN=1576101746//-->
|
||||
<!--TITLE=Michael Abrash's Graphics Programming Black Book Special Edition//-->
|
||||
<!--AUTHOR=Michael Abrash//-->
|
||||
<!--PUBLISHER=The Coriolis Group, Inc.//-->
|
||||
<title>Michael Abrash's Graphics Programming Black Book Special Edition: Pushing the 286 and 386</title>
|
||||
<meta name="chapter" content="11" />
|
||||
<meta name="pages" content="212-216" />
|
||||
<!--UNASSIGNED1//-->
|
||||
<!--UNASSIGNED2//-->
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
|
@ -65,7 +58,7 @@
|
|||
|
||||
<p>Of course, those are exactly the rules that apply to 8088 optimization as well. Isn’t it convenient that the same general rules apply across the board?</p>
|
||||
|
||||
<h4 align="center"><a id="Heading7"></a>Data Alignment</h4>
|
||||
<h4 align="center" id="Heading7">Data Alignment</h4>
|
||||
|
||||
<p>Thanks to its 16-bit bus, the 286 can access word-sized memory variables just as fast as byte-sized variables. There’s a catch, however: That’s only true for word-sized variables that start at even addresses. When the 286 is asked to perform a word-sized access starting at an odd address, it actually performs two separate accesses, each of which fetches 1 byte, just as the 8088 does for all word-sized accesses.</p>
|
||||
|
||||
|
|
@ -83,15 +76,14 @@
|
|||
|
||||
<p>That, in a nutshell, is the data alignment cycle-eater, the one new cycle-eater of the 286 and 386. (The data alignment cycle-eater is a close relative of the 8088’s 8-bit bus cycle-eater, but since it behaves differently—occurring only at odd addresses—and is avoided with a different workaround, we’ll consider it to be a new cycle-eater.)</p>
|
||||
|
||||
<p><a id="Fig1"><!-- </A><A HREF="javascript:displayWindow('images/11-01.jpg',409,328 )"> --><img src="images/11-01.jpg" /><br />
|
||||
<!-- </A>
|
||||
<BR><A HREF="javascript:displayWindow('images/11-01.jpg',409,328)"> --><b>Figure 11.1</b></a> <i>The data alignment cycle-eater.</i></p>
|
||||
<p><a id="Fig1"><img src="images/11-01.jpg" /><br />
|
||||
<b>Figure 11.1</b></a> <i>The data alignment cycle-eater.</i></p>
|
||||
|
||||
<p>The way to deal with the data alignment cycle-eater is straightforward: <i>Don’t perform word-sized accesses to odd addresses on the 286 if you can help it</i>. The easiest way to avoid the data alignment cycle-eater is to place the directive <b>EVEN</b> before each of your word-sized variables. <b>EVEN</b> forces the offset of the next byte assembled to be even by inserting a <b>NOP</b> if the current offset is odd; consequently, you can ensure that any word-sized variable can be accessed efficiently by the 286 simply by preceding it with <b>EVEN</b>.</p>
|
||||
|
||||
<p>Listing 11.2, which accesses memory a word at a time with each word starting at an odd address, runs on a 10 MHz AT clone in 1.27 ms per repetition of <b>MOVSW</b>, or 0.64 ms per word-sized memory access. That’s 6-plus cycles per word-sized access, which breaks down to two separate memory accesses—3 cycles to access the high byte of each word and 3 cycles to access the low byte of each word, the inevitable result of non-word-aligned word-sized memory accesses—plus a bit extra for DRAM refresh.</p>
|
||||
|
||||
<p><b>LISTING 11.2 L11-2.ASM</b></p><!-- CODE //-->
|
||||
<p><b>LISTING 11.2 L11-2.ASM</b></p>
|
||||
<pre>
|
||||
;
|
||||
; *** Listing 11.2 ***
|
||||
|
|
@ -110,11 +102,11 @@ Skip:
|
|||
call ZTimerOn
|
||||
rep movsw
|
||||
call ZTimerOff
|
||||
</pre><!-- END CODE //-->
|
||||
</pre>
|
||||
|
||||
<p>On the other hand, Listing 11.3, which is exactly the same as Listing 11.2 save that the memory accesses are word-aligned (start at even addresses), runs in 0.64 ms per repetition of <b>MOVSW</b>, or 0.32 µs per word-sized memory access. That’s 3 cycles per word-sized access—exactly twice as fast as the non-word-aligned accesses of Listing 11.2, just as we predicted.</p>
|
||||
|
||||
<p><b>LISTING 11.3 L11-3.ASM</b></p><!-- CODE //-->
|
||||
<p><b>LISTING 11.3 L11-3.ASM</b></p>
|
||||
<pre>
|
||||
;
|
||||
; *** Listing 11.3 ***
|
||||
|
|
@ -132,11 +124,11 @@ Skip:
|
|||
call ZTimerOn
|
||||
rep movsw
|
||||
call ZTimerOff
|
||||
</pre><!-- END CODE //-->
|
||||
</pre>
|
||||
|
||||
<p>The data alignment cycle-eater has intriguing implications for speeding up 286/386 code. The expenditure of a little care and a few bytes to make sure that word-sized variables and memory blocks are word-aligned can literally double the performance of certain code running on the 286. Even if it doesn’t double performance, word alignment usually helps and never hurts.</p>
|
||||
|
||||
<h4 align="left"><a id="Heading8"></a>Code Alignment</h4>
|
||||
<h4 align="left" id="Heading8">Code Alignment</h4>
|
||||
|
||||
<p>Lack of word alignment can also interfere with instruction fetching on the 286, although not to the extent that it interferes with access to word-sized memory variables. The 286 prefetches instructions a word at a time; even if a given instruction doesn’t begin at an even address, the 286 simply fetches the first byte of that instruction at the same time that it fetches the last byte of the previous instruction, as shown in Figure 11.2, then separates the bytes internally. That means that in most cases, instructions run just as fast whether they’re word-aligned or not.</p>
|
||||
|
||||
|
|
@ -159,10 +151,6 @@ Skip:
|
|||
|
||||
<div align="center">
|
||||
Graphics Programming Black Book © 2001 Michael Abrash
|
||||
</div><!-- all of the reference materials (books) have the footer and subfoot reveresed -->
|
||||
<!-- reference_subfoot = footer -->
|
||||
<!-- reference_footer = subfoot -->
|
||||
<!-- BEGIN SUB FOOTER -->
|
||||
<!-- END FOOTER -->
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
45
11-04.html
45
11-04.html
|
|
@ -10,16 +10,9 @@
|
|||
<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: Pushing the 286 and 386</title><!-- HEADER -->
|
||||
<!-- Empty Reference Subhead -->
|
||||
<!--ISBN=1576101746//-->
|
||||
<!--TITLE=Michael Abrash's Graphics Programming Black Book Special Edition//-->
|
||||
<!--AUTHOR=Michael Abrash//-->
|
||||
<!--PUBLISHER=The Coriolis Group, Inc.//-->
|
||||
<title>Michael Abrash's Graphics Programming Black Book Special Edition: Pushing the 286 and 386</title>
|
||||
<meta name="chapter" content="11" />
|
||||
<meta name="pages" content="216-220" />
|
||||
<!--UNASSIGNED1//-->
|
||||
<!--UNASSIGNED2//-->
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
|
@ -37,7 +30,7 @@
|
|||
|
||||
<p><br /></p>
|
||||
|
||||
<p>When I was developing the Zen timer, I used my trusty 10 MHz 286-based AT clone to verify the basic functionality of the timer by measuring the performance of simple instruction sequences. I was cruising along with no problems until I timed the following code:</p><!-- CODE SNIP //-->
|
||||
<p>When I was developing the Zen timer, I used my trusty 10 MHz 286-based AT clone to verify the basic functionality of the timer by measuring the performance of simple instruction sequences. I was cruising along with no problems until I timed the following code:</p>
|
||||
<pre>
|
||||
|
||||
mov cx,1000
|
||||
|
|
@ -45,19 +38,17 @@
|
|||
LoopTop:
|
||||
loop LoopTop
|
||||
call ZTimerOff
|
||||
</pre><!-- END CODE SNIP //-->
|
||||
</pre>
|
||||
|
||||
<p><a id="Fig2"><!-- </A><A HREF="javascript:displayWindow('images/11-02.jpg',406,204 )"> --><img src="images/11-02.jpg" /><br />
|
||||
<!-- </A>
|
||||
<BR><A HREF="javascript:displayWindow('images/11-02.jpg',406,204)"> --><b>Figure 11.2</b></a> <i>Word-aligned prefetching on the 286.</i></p>
|
||||
<p><a id="Fig2"><img src="images/11-02.jpg" /><br />
|
||||
<b>Figure 11.2</b></a> <i>Word-aligned prefetching on the 286.</i></p>
|
||||
|
||||
<p><a id="Fig3"><!-- </A><A HREF="javascript:displayWindow('images/11-03.jpg',408,240 )"> --><img src="images/11-03.jpg" /><br />
|
||||
<!-- </A>
|
||||
<BR><A HREF="javascript:displayWindow('images/11-03.jpg',408,240)"> --><b>Figure 11.3</b></a> <i>How instruction bytes are fetched after a branch.</i></p>
|
||||
<p><a id="Fig3"><img src="images/11-03.jpg" /><br />
|
||||
<b>Figure 11.3</b></a> <i>How instruction bytes are fetched after a branch.</i></p>
|
||||
|
||||
<p>Now, this code <i>should</i> run in, say, about 12 cycles per loop at most. Instead, it took over 14 cycles per loop, an execution time that I could not explain in any way. After rolling it around in my head for a while, I took a look at the code under a debugger...and the answer leaped out at me. <i>The loop began at an odd address!</i> That meant that two instruction fetches were required each time through the loop; one to get the opcode byte of the <b>LOOP</b> instruction, which resided at the end of one word-aligned word, and another to get the displacement byte, which resided at the start of the next word-aligned word.</p>
|
||||
|
||||
<p>One simple change brought the execution time down to a reasonable 12.5 cycles per loop:</p><!-- CODE SNIP //-->
|
||||
<p>One simple change brought the execution time down to a reasonable 12.5 cycles per loop:</p>
|
||||
<pre>
|
||||
mov cx,1000
|
||||
call ZTimerOn
|
||||
|
|
@ -65,7 +56,7 @@ LoopTop:
|
|||
LoopTop:
|
||||
loop LoopTop
|
||||
call ZTimerOff
|
||||
</pre><!-- END CODE SNIP //-->
|
||||
</pre>
|
||||
|
||||
<p>While word-aligning branch destinations can improve branching performance, it’s a nuisance and can increase code size a good deal, so it’s not worth doing in most code. Besides, <b>EVEN</b> inserts a <b>NOP</b> instruction if necessary, and the time required to execute a <b>NOP</b> can sometimes cancel the performance advantage of having a word-aligned branch destination.</p>
|
||||
|
||||
|
|
@ -77,16 +68,16 @@ LoopTop:
|
|||
</tr>
|
||||
</table>
|
||||
|
||||
<p>I recommend that you only go out of your way to word-align the start offsets of your subroutines, as in:</p><!-- CODE SNIP //-->
|
||||
<p>I recommend that you only go out of your way to word-align the start offsets of your subroutines, as in:</p>
|
||||
<pre>
|
||||
even
|
||||
FindChar proc near
|
||||
:
|
||||
</pre><!-- END CODE SNIP //-->
|
||||
</pre>
|
||||
|
||||
<p>In my experience, this simple practice is the one form of code alignment that consistently provides a reasonable return for bytes and effort expended, although sometimes it also pays to word-align tight time-critical loops.</p>
|
||||
|
||||
<h4 align="center"><a id="Heading9"></a>Alignment and the 386</h4>
|
||||
<h4 align="center" id="Heading9">Alignment and the 386</h4>
|
||||
|
||||
<p>So far we’ve only discussed alignment as it pertains to the 286. What, you may well ask, of the 386?</p>
|
||||
|
||||
|
|
@ -94,7 +85,7 @@ FindChar proc near
|
|||
|
||||
<p>As for code alignment...the subroutine-start word-alignment rule of the 286 serves reasonably well there too since it avoids the worst case, where just 1 byte is fetched on entry to a subroutine. While optimum performance would dictate doubleword alignment of subroutines, that takes 3 bytes, a high price to pay for an optimization that improves performance <i>only</i> on the post 286 processors.</p>
|
||||
|
||||
<h4 align="center"><a id="Heading10"></a>Alignment and the Stack</h4>
|
||||
<h4 align="center" id="Heading10">Alignment and the Stack</h4>
|
||||
|
||||
<p>One side-effect of the data alignment cycle-eater of the 286 and 386 is that you should <i>never</i> allow the stack pointer to become odd. (You can make the stack pointer odd by adding an odd value to it or subtracting an odd value from it, or by loading it with an odd value.) An odd stack pointer on the 286 or 386 (or a non-doubleword-aligned stack in 32-bit protected mode on the 386, 486, or Pentium) will significantly reduce the performance of <b>PUSH,</b> <b>POP,</b> <b>CALL</b>, and <b>RET</b>, as well as <b>INT</b> and <b>IRET</b>, which are executed to invoke DOS and BIOS functions, handle keystrokes and incoming serial characters, and manage the mouse. I know of a Forth programmer who vastly improved the performance of a complex application on the AT simply by forcing the Forth interpreter to maintain an even stack pointer at all times.</p>
|
||||
|
||||
|
|
@ -108,7 +99,7 @@ FindChar proc near
|
|||
</tr>
|
||||
</table>
|
||||
|
||||
<h4 align="center"><a id="Heading11"></a>The DRAM Refresh Cycle-Eater: Still an Act of God</h4>
|
||||
<h4 align="center" id="Heading11">The DRAM Refresh Cycle-Eater: Still an Act of God</h4>
|
||||
|
||||
<p>The DRAM refresh cycle-eater is the cycle-eater that’s least changed from its 8088 form on the 286 and 386. In the AT, DRAM refresh uses a little over five percent of all available memory accesses, slightly less than it uses in the PC, but in the same ballpark. While the DRAM refresh penalty varies somewhat on various AT clones and 386 computers (in fact, a few computers are built around static RAM, which requires no refresh at all; likewise, caches are made of static RAM so cached systems generally suffer less from DRAM refresh), the 5 percent figure is a good rule of thumb.</p>
|
||||
|
||||
|
|
@ -116,7 +107,7 @@ FindChar proc near
|
|||
|
||||
<p>There’s nothing much new with DRAM refresh on 286/386 computers, then. Be aware of it, but don’t overly concern yourself—DRAM refresh is still an act of God, and there’s not a blessed thing you can do about it. Happily, the internal caches of the 486 and Pentium make DRAM refresh largely a performance non-issue on those processors.</p>
|
||||
|
||||
<h4 align="center"><a id="Heading12"></a>The Display Adapter Cycle-Eater</h4>
|
||||
<h4 align="center" id="Heading12">The Display Adapter Cycle-Eater</h4>
|
||||
|
||||
<p>Finally we come to the last of the cycle-eaters, the display adapter cycle-eater. There are two ways of looking at this cycle-eater on 286/386 computers: (1) It’s much worse than it was on the PC, or (2) it’s just about the same as it was on the PC.</p>
|
||||
|
||||
|
|
@ -141,10 +132,6 @@ FindChar proc near
|
|||
|
||||
<div align="center">
|
||||
Graphics Programming Black Book © 2001 Michael Abrash
|
||||
</div><!-- all of the reference materials (books) have the footer and subfoot reveresed -->
|
||||
<!-- reference_subfoot = footer -->
|
||||
<!-- reference_footer = subfoot -->
|
||||
<!-- BEGIN SUB FOOTER -->
|
||||
<!-- END FOOTER -->
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
19
11-05.html
19
11-05.html
|
|
@ -10,16 +10,9 @@
|
|||
<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: Pushing the 286 and 386</title><!-- HEADER -->
|
||||
<!-- Empty Reference Subhead -->
|
||||
<!--ISBN=1576101746//-->
|
||||
<!--TITLE=Michael Abrash's Graphics Programming Black Book Special Edition//-->
|
||||
<!--AUTHOR=Michael Abrash//-->
|
||||
<!--PUBLISHER=The Coriolis Group, Inc.//-->
|
||||
<title>Michael Abrash's Graphics Programming Black Book Special Edition: Pushing the 286 and 386</title>
|
||||
<meta name="chapter" content="11" />
|
||||
<meta name="pages" content="220-222" />
|
||||
<!--UNASSIGNED1//-->
|
||||
<!--UNASSIGNED2//-->
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
|
@ -61,7 +54,7 @@
|
|||
|
||||
<p>What can we do about this new, more virulent form of the display adapter cycle-eater? The workaround is the same as it was on the PC: Access display memory as little as you possibly can.</p>
|
||||
|
||||
<h4 align="left"><a id="Heading13"></a>New Instructions and Features: The 286</h4>
|
||||
<h4 align="left" id="Heading13">New Instructions and Features: The 286</h4>
|
||||
|
||||
<p>The 286 and 386 offer a number of new instructions. The 286 has a relatively small number of instructions that the 8088 lacks, while the 386 has those instructions and quite a few more, along with new addressing modes and data sizes. We’ll discuss the 286 and the 386 separately in this regard.</p>
|
||||
|
||||
|
|
@ -71,7 +64,7 @@
|
|||
|
||||
<p>A couple of old instructions gain new features on the 286. For one, the 286 version of <b>PUSH</b> is capable of pushing a constant on the stack. For another, the 286 allows all shifts and rotates to be performed for not just 1 bit or the number of bits specified by CL, but for <i>any</i> constant number of bits.</p>
|
||||
|
||||
<h4 align="left"><a id="Heading14"></a>New Instructions and Features: The 386</h4>
|
||||
<h4 align="left" id="Heading14">New Instructions and Features: The 386</h4>
|
||||
|
||||
<p>The 386 is somewhat more complex than the 286 regarding new features. Once again, we won’t discuss protected mode, which on the 386 comes with the ability to address up to 4 gigabytes per segment and 64 terabytes in all. In real mode (and in virtual-86 mode, which allows the 386 to multitask MS-DOS applications, and which is identical to real mode so far as MS-DOS programs are concerned), programs running on the 386 are still limited to 1 MB of addressable memory and 64K per segment.</p>
|
||||
|
||||
|
|
@ -98,10 +91,6 @@
|
|||
|
||||
<div align="center">
|
||||
Graphics Programming Black Book © 2001 Michael Abrash
|
||||
</div><!-- all of the reference materials (books) have the footer and subfoot reveresed -->
|
||||
<!-- reference_subfoot = footer -->
|
||||
<!-- reference_footer = subfoot -->
|
||||
<!-- BEGIN SUB FOOTER -->
|
||||
<!-- END FOOTER -->
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
23
11-06.html
23
11-06.html
|
|
@ -10,16 +10,9 @@
|
|||
<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: Pushing the 286 and 386</title><!-- HEADER -->
|
||||
<!-- Empty Reference Subhead -->
|
||||
<!--ISBN=1576101746//-->
|
||||
<!--TITLE=Michael Abrash's Graphics Programming Black Book Special Edition//-->
|
||||
<!--AUTHOR=Michael Abrash//-->
|
||||
<!--PUBLISHER=The Coriolis Group, Inc.//-->
|
||||
<title>Michael Abrash's Graphics Programming Black Book Special Edition: Pushing the 286 and 386</title>
|
||||
<meta name="chapter" content="11" />
|
||||
<meta name="pages" content="222-224" />
|
||||
<!--UNASSIGNED1//-->
|
||||
<!--UNASSIGNED2//-->
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
|
@ -47,7 +40,7 @@
|
|||
</tr>
|
||||
</table>
|
||||
|
||||
<h4 align="center"><a id="Heading15"></a>Optimization Rules: The More Things Change...</h4>
|
||||
<h4 align="center" id="Heading15">Optimization Rules: The More Things Change...</h4>
|
||||
|
||||
<p>Let’s see what we’ve learned about 286/386 optimization. Mostly what we’ve learned is that our familiar PC cycle-eaters still apply, although in somewhat different forms, and that the major optimization rules for the PC hold true on ATs and 386-based computers. You won’t go wrong on any of these computers if you keep your instructions short, use the registers heavily and avoid memory, don’t branch, and avoid accessing display memory like the plague.</p>
|
||||
|
||||
|
|
@ -55,7 +48,7 @@
|
|||
|
||||
<p>There’s one cycle-eater with new implications on the 286 and 386, and that’s the data alignment cycle-eater. From the data alignment cycle-eater we get a new rule: Word-align your word-sized variables, and start your subroutines at even addresses.</p>
|
||||
|
||||
<h4 align="center"><a id="Heading16"></a>Detailed Optimization</h4>
|
||||
<h4 align="center" id="Heading16">Detailed Optimization</h4>
|
||||
|
||||
<p>While the major 8088 optimization rules hold true on computers built around the 286 and 386, many of the instruction-specific optimizations no longer hold, for the execution times of most instructions are quite different on the 286 and 386 than on the 8088. We have already seen one such example of the sometimes vast difference between 8088 and 286/386 instruction execution times: <b>MOV [WordVar],0</b>, which has an Execution Unit execution time of 20 cycles on the 8088, has an EU execution time of just 3 cycles on the 286 and 2 cycles on the 386.</p>
|
||||
|
||||
|
|
@ -71,7 +64,7 @@
|
|||
|
||||
<p>Theory confirmed.</p>
|
||||
|
||||
<p><b>LISTING 11.4 L11-4.ASM</b></p><!-- CODE //-->
|
||||
<p><b>LISTING 11.4 L11-4.ASM</b></p>
|
||||
<pre>
|
||||
;
|
||||
; *** Listing 11.4 ***
|
||||
|
|
@ -85,7 +78,7 @@
|
|||
add dx,100h
|
||||
endm
|
||||
call ZTimerOff
|
||||
</pre><!-- END CODE //-->
|
||||
</pre>
|
||||
|
||||
<p><br /></p>
|
||||
|
||||
|
|
@ -104,10 +97,6 @@
|
|||
|
||||
<div align="center">
|
||||
Graphics Programming Black Book © 2001 Michael Abrash
|
||||
</div><!-- all of the reference materials (books) have the footer and subfoot reveresed -->
|
||||
<!-- reference_subfoot = footer -->
|
||||
<!-- reference_footer = subfoot -->
|
||||
<!-- BEGIN SUB FOOTER -->
|
||||
<!-- END FOOTER -->
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
21
11-07.html
21
11-07.html
|
|
@ -10,16 +10,9 @@
|
|||
<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: Pushing the 286 and 386</title><!-- HEADER -->
|
||||
<!-- Empty Reference Subhead -->
|
||||
<!--ISBN=1576101746//-->
|
||||
<!--TITLE=Michael Abrash's Graphics Programming Black Book Special Edition//-->
|
||||
<!--AUTHOR=Michael Abrash//-->
|
||||
<!--PUBLISHER=The Coriolis Group, Inc.//-->
|
||||
<title>Michael Abrash's Graphics Programming Black Book Special Edition: Pushing the 286 and 386</title>
|
||||
<meta name="chapter" content="11" />
|
||||
<meta name="pages" content="224-226" />
|
||||
<!--UNASSIGNED1//-->
|
||||
<!--UNASSIGNED2//-->
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
|
@ -37,7 +30,7 @@
|
|||
|
||||
<p><br /></p>
|
||||
|
||||
<p><b>LISTING 11.5 L11-5.ASM</b></p><!-- CODE //-->
|
||||
<p><b>LISTING 11.5 L11-5.ASM</b></p>
|
||||
<pre>
|
||||
;
|
||||
; *** Listing 11.5 ***
|
||||
|
|
@ -58,7 +51,7 @@ Skip:
|
|||
add [WordVar]100h
|
||||
endm
|
||||
call ZTimerOff
|
||||
</pre><!-- END CODE //-->
|
||||
</pre>
|
||||
|
||||
<p>What’s going on? Simply this: Instruction fetching is controlling overall execution time on <i>both</i> processors. Both the 8088 in a PC and the 286 in an AT can execute the bytes of the instructions in Listings 11.4 and 11.5 faster than they can be fetched. Since the instructions are exactly the same lengths on both processors, it stands to reason that the ratio of the overall execution times of the instructions should be the same on both processors as well. Instruction length controls execution time, and the instruction lengths are the same—therefore the ratios of the execution times are the same. The 286 can both fetch and execute instruction bytes faster than the 8088 can, so code executes much faster on the 286; nonetheless, because the 286 can also execute those instruction bytes much faster than it can fetch them, overall performance is still largely determined by the size of the instructions.</p>
|
||||
|
||||
|
|
@ -68,7 +61,7 @@ Skip:
|
|||
|
||||
<p>The more things change, the more they remain the same....</p>
|
||||
|
||||
<h4 align="left"><a id="Heading17"></a>POPF and the 286</h4>
|
||||
<h4 align="left" id="Heading17">POPF and the 286</h4>
|
||||
|
||||
<p>We’ve one final 286-related item to discuss: the hardware malfunction of <b>POPF</b> under certain circumstances on the 286.</p>
|
||||
|
||||
|
|
@ -97,10 +90,6 @@ Skip:
|
|||
|
||||
<div align="center">
|
||||
Graphics Programming Black Book © 2001 Michael Abrash
|
||||
</div><!-- all of the reference materials (books) have the footer and subfoot reveresed -->
|
||||
<!-- reference_subfoot = footer -->
|
||||
<!-- reference_footer = subfoot -->
|
||||
<!-- BEGIN SUB FOOTER -->
|
||||
<!-- END FOOTER -->
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
46
11-08.html
46
11-08.html
|
|
@ -10,16 +10,9 @@
|
|||
<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: Pushing the 286 and 386</title><!-- HEADER -->
|
||||
<!-- Empty Reference Subhead -->
|
||||
<!--ISBN=1576101746//-->
|
||||
<!--TITLE=Michael Abrash's Graphics Programming Black Book Special Edition//-->
|
||||
<!--AUTHOR=Michael Abrash//-->
|
||||
<!--PUBLISHER=The Coriolis Group, Inc.//-->
|
||||
<title>Michael Abrash's Graphics Programming Black Book Special Edition: Pushing the 286 and 386</title>
|
||||
<meta name="chapter" content="11" />
|
||||
<meta name="pages" content="226-231" />
|
||||
<!--UNASSIGNED1//-->
|
||||
<!--UNASSIGNED2//-->
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
|
@ -41,11 +34,10 @@
|
|||
|
||||
<p>Obviously, the segment:offset that <b>IRET</b> expects to find on the stack above the pushed flags isn’t present when the stack is set up for <b>POPF</b>, so we’ll have to adjust the stack a bit before we can substitute <b>IRET</b> for <b>POPF</b>. What we’ll have to do is push the segment:offset of the instruction after our workaround code onto the stack right above the pushed flags. <b>IRET</b> will then branch to that address and pop the flags, ending up at the instruction after the workaround code with the flags popped. That’s just the result that would have occurred had we executed <b>POPF</b>—WITH the bonus that no interrupts can accidentally occur when the Interrupt flag is 0 both before and after the pop.</p>
|
||||
|
||||
<p><a id="Fig4"><!-- </A><A HREF="javascript:displayWindow('images/11-04.jpg',412,383 )"> --><img src="images/11-04.jpg" /><br />
|
||||
<!-- </A>
|
||||
<BR><A HREF="javascript:displayWindow('images/11-04.jpg',412,383)"> --><b>Figure 11.4</b></a> <i>The operation of POPF.</i></p>
|
||||
<p><a id="Fig4"><img src="images/11-04.jpg" /><br />
|
||||
<b>Figure 11.4</b></a> <i>The operation of POPF.</i></p>
|
||||
|
||||
<p>How can we push the segment:offset of the next instruction? Well, finding the offset of the next instruction by performing a near call to that instruction is a tried-and-true trick. We can do something similar here, but in this case we need a far call, since <b>IRET</b> requires both a segment and an offset. We’ll also branch backward so that the address pushed on the stack will point to the instruction we want to continue with. The code works out like this:</p><!-- CODE //-->
|
||||
<p>How can we push the segment:offset of the next instruction? Well, finding the offset of the next instruction by performing a near call to that instruction is a tried-and-true trick. We can do something similar here, but in this case we need a far call, since <b>IRET</b> requires both a segment and an offset. We’ll also branch backward so that the address pushed on the stack will point to the instruction we want to continue with. The code works out like this:</p>
|
||||
<pre>
|
||||
jmpshort popfskip
|
||||
popfiret:
|
||||
|
|
@ -63,15 +55,14 @@ popfskip:
|
|||
; the word that was on top of the stack when JMP SHORT POPFSKIP
|
||||
; was reached has been popped into the FLAGS register, just as
|
||||
; if a POPF instruction had been executed.
|
||||
</pre><!-- END CODE //-->
|
||||
</pre>
|
||||
|
||||
<p><a id="Fig5"><!-- </A><A HREF="javascript:displayWindow('images/11-05.jpg',410,520 )"> --><img src="images/11-05.jpg" /><br />
|
||||
<!-- </A>
|
||||
<BR><A HREF="javascript:displayWindow('images/11-05.jpg',410,520)"> --><b>Figure 11.5</b></a> <i>The operation of IRET.</i></p>
|
||||
<p><a id="Fig5"><img src="images/11-05.jpg" /><br />
|
||||
<b>Figure 11.5</b></a> <i>The operation of IRET.</i></p>
|
||||
|
||||
<p>The operation of this code is illustrated in Figure 11.6.</p>
|
||||
|
||||
<p>The <b>POPF</b> workaround can best be implemented as a macro; we can also emulate a far call by pushing CS and performing a near call, thereby shrinking the workaround code by 1 byte:</p><!-- CODE //-->
|
||||
<p>The <b>POPF</b> workaround can best be implemented as a macro; we can also emulate a far call by pushing CS and performing a near call, thereby shrinking the workaround code by 1 byte:</p>
|
||||
<pre>
|
||||
EMULATE_POPF macro
|
||||
local popfskip, popfiret
|
||||
|
|
@ -82,9 +73,9 @@ popfskip:
|
|||
push cs
|
||||
call popfiret
|
||||
endm
|
||||
</pre><!-- END CODE //-->
|
||||
</pre>
|
||||
|
||||
<p>By the way, the flags can be popped much more quickly if you’re willing to alter a register in the process. For example, the following macro emulates <b>POPF</b> with just one branch, but wipes out AX:</p><!-- CODE SNIP //-->
|
||||
<p>By the way, the flags can be popped much more quickly if you’re willing to alter a register in the process. For example, the following macro emulates <b>POPF</b> with just one branch, but wipes out AX:</p>
|
||||
<pre>
|
||||
EMULATE_POPF_TRASH_AX macro
|
||||
push cs
|
||||
|
|
@ -92,9 +83,9 @@ EMULATE_POPF_TRASH_AX macro
|
|||
push ax
|
||||
iret
|
||||
endm
|
||||
</pre><!-- END CODE SNIP //-->
|
||||
</pre>
|
||||
|
||||
<p>It’s not a perfect substitute for <b>POPF</b>, since <b>POPF</b> doesn’t alter any registers, but it’s faster and shorter than <b>EMULATE_POPF</b> when you can spare the register. If you’re using 286-specific instructions, you can use which is shorter still, alters no registers, and branches just once. (Of course, this version of <b>EMULATE_POPF</b> won’t work on an 8088.)</p><!-- CODE SNIP //-->
|
||||
<p>It’s not a perfect substitute for <b>POPF</b>, since <b>POPF</b> doesn’t alter any registers, but it’s faster and shorter than <b>EMULATE_POPF</b> when you can spare the register. If you’re using 286-specific instructions, you can use which is shorter still, alters no registers, and branches just once. (Of course, this version of <b>EMULATE_POPF</b> won’t work on an 8088.)</p>
|
||||
<pre>
|
||||
.286
|
||||
:
|
||||
|
|
@ -103,11 +94,10 @@ EMULATE_POPFmacro
|
|||
pushoffset $+4
|
||||
iret
|
||||
endm
|
||||
</pre><!-- END CODE SNIP //-->
|
||||
</pre>
|
||||
|
||||
<p><a id="Fig6"><!-- </A><A HREF="javascript:displayWindow('images/11-06.jpg',409,447 )"> --><img src="images/11-06.jpg" /><br />
|
||||
<!-- </A>
|
||||
<BR><A HREF="javascript:displayWindow('images/11-06.jpg',409,447)"> --><b>Figure 11.6</b></a> <i>Workaround code for the POPF bug.</i></p>
|
||||
<p><a id="Fig6"><img src="images/11-06.jpg" /><br />
|
||||
<b>Figure 11.6</b></a> <i>Workaround code for the POPF bug.</i></p>
|
||||
|
||||
<p>The standard version of <b>EMULATE_POPF</b> is 6 bytes longer than <b>POPF</b> and much slower, as you’d expect given that it involves three branches. Anyone in his/her right mind would prefer <b>POPF</b> to a larger, slower, three-branch macro—given a choice. In noncode, however, there’s no choice here; the safer—if slower—approach is the best. (Having people associate your programs with crashed computers is <i>not</i> a desirable situation, no matter how unfair the circumstances under which it occurs.)</p>
|
||||
|
||||
|
|
@ -130,10 +120,6 @@ EMULATE_POPFmacro
|
|||
|
||||
<div align="center">
|
||||
Graphics Programming Black Book © 2001 Michael Abrash
|
||||
</div><!-- all of the reference materials (books) have the footer and subfoot reveresed -->
|
||||
<!-- reference_subfoot = footer -->
|
||||
<!-- reference_footer = subfoot -->
|
||||
<!-- BEGIN SUB FOOTER -->
|
||||
<!-- END FOOTER -->
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
33
12-01.html
33
12-01.html
|
|
@ -10,16 +10,9 @@
|
|||
<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: Pushing the 486</title><!-- HEADER -->
|
||||
<!-- Empty Reference Subhead -->
|
||||
<!--ISBN=1576101746//-->
|
||||
<!--TITLE=Michael Abrash's Graphics Programming Black Book Special Edition//-->
|
||||
<!--AUTHOR=Michael Abrash//-->
|
||||
<!--PUBLISHER=The Coriolis Group, Inc.//-->
|
||||
<title>Michael Abrash's Graphics Programming Black Book Special Edition: Pushing the 486</title>
|
||||
<meta name="chapter" content="12" />
|
||||
<meta name="pages" content="233-237" />
|
||||
<!--UNASSIGNED1//-->
|
||||
<!--UNASSIGNED2//-->
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
|
@ -37,10 +30,10 @@
|
|||
|
||||
<p><br /></p>
|
||||
|
||||
<h2><a id="Heading1"></a>Chapter 12<br />
|
||||
<h2 id="Heading1">Chapter 12<br />
|
||||
Pushing the 486</h2>
|
||||
|
||||
<h3><a id="Heading2"></a>It’s Not Just a Bigger 386</h3>
|
||||
<h3 id="Heading2">It’s Not Just a Bigger 386</h3>
|
||||
|
||||
<p>So this traveling salesman is walking down a road, and he sees a group of men digging a ditch with their bare hands. “Whoa, there!” he says. “What you guys need is a Model 8088 ditch digger!” And he whips out a trowel and sells it to them.</p>
|
||||
|
||||
|
|
@ -52,13 +45,13 @@
|
|||
|
||||
<p>Substitute “processor” for the various digging implements, and you get an idea of just how different the optimization rules for the 486 are from what you’re used to. Okay, it’s not quite <i>that</i> bad—but upon encountering a processor where string instructions are often to be avoided and memory-to-register <b>MOV</b>s are frequently as fast as register-to-register <b>MOV</b>s, Dorothy was heard to exclaim (before she sank out of sight in a swirl of hopelessly mixed metaphors), “I don’t think we’re in Kansas anymore, Toto.”</p>
|
||||
|
||||
<h4 align="left"><a id="Heading3"></a>Enter the 486</h4>
|
||||
<h4 align="left" id="Heading3">Enter the 486</h4>
|
||||
|
||||
<p>No chip that is a direct, fully compatible descendant of the 8088, 286, and 386 could ever be called a RISC chip, but the 486 certainly contains RISC elements, and it’s those elements that are most responsible for making 486 optimization unique. Simple, common instructions are executed in a single cycle by a RISC-like core processor, but other instructions are executed pretty much as they were on the 386, where every instruction takes at least 2 cycles. For example, <b>MOV AL, [TestChar]</b> takes only 1 cycle on the 486, assuming both instruction and data are in the cache—3 cycles faster than the 386—but <b>STOSB</b> takes 5 cycles, 1 cycle <i>slower</i> than on the 386. The floating-point execution unit inside the 486 is also much faster than the 387 math coprocessor, largely because, being in the same silicon as the CPU (the 486 has a math coprocessor built in), it is more tightly coupled. The results are sometimes startling: <b>FMUL</b> (floating point multiply) is usually faster on the 486 than <b>IMUL</b> (integer multiply)!</p>
|
||||
|
||||
<p>An encyclopedic approach to 486 optimization would take a book all by itself, so in this chapter I’m only going to hit the highlights of 486 optimization, touching on several optimization rules, some documented, some not. You might also want to check out the following sources of 486 information: <i>i486 Microprocessor Programmer’s Reference Manual,</i> from Intel; “8086 Optimization: Aim Down the Middle and Pray,” in the March, 1991 <i>Dr. Dobb’s Journal</i>; and “Peak Performance: On to the 486,” in the November, 1990 <i>Programmer’s Journal.</i></p>
|
||||
|
||||
<h3><a id="Heading4"></a>Rules to Optimize By</h3>
|
||||
<h3 id="Heading4">Rules to Optimize By</h3>
|
||||
|
||||
<p>In Appendix G of the <i>i486 Microprocessor Programmer</i>’<i>s</i> <i>Reference Manual</i>, Intel lists a number of optimization techniques for the 486. While neither exhaustive (we’ll look at two undocumented optimizations shortly) nor entirely accurate (we’ll correct two of the rules here), Intel’s list is certainly a good starting point. In particular, the list conveys the extent to which 486 optimization differs from optimization for earlier x86 processors. Generally, I’ll be discussing optimization for real mode (it being the most widely used mode at the moment), although many of the rules should apply to protected mode as well.</p>
|
||||
|
||||
|
|
@ -72,7 +65,7 @@
|
|||
|
||||
<p>In other words, for cached code (which time-critical code almost always is), performance is predictable and can be calculated with good precision, and those calculations will apply on any 486. However, “predictable” doesn’t mean “trivial”; the cycle times printed for the various instructions are not the whole story. You must be aware of all the rules, documented and undocumented, that go into calculating actual execution times—and uncovering some of those rules is exactly what this chapter is about.</p>
|
||||
|
||||
<h4 align="left"><a id="Heading5"></a>The Hazards of Indexed Addressing</h4>
|
||||
<h4 align="left" id="Heading5">The Hazards of Indexed Addressing</h4>
|
||||
|
||||
<p>Rule #1: Avoid indexed addressing (that is, try not to use either two registers or scaled addressing to point to memory).</p>
|
||||
|
||||
|
|
@ -88,16 +81,16 @@
|
|||
</tr>
|
||||
</table>
|
||||
|
||||
<p>As an example, you might adhere to this rule by replacing the code</p><!-- CODE SNIP //-->
|
||||
<p>As an example, you might adhere to this rule by replacing the code</p>
|
||||
<pre>
|
||||
LoopTop:
|
||||
add ax,[bx+si]
|
||||
add si,2
|
||||
dec cx
|
||||
jnz LoopTop
|
||||
</pre><!-- END CODE SNIP //-->
|
||||
</pre>
|
||||
|
||||
<p>with this</p><!-- CODE SNIP //-->
|
||||
<p>with this</p>
|
||||
<pre>
|
||||
add si,bx
|
||||
LoopTop:
|
||||
|
|
@ -106,7 +99,7 @@ LoopTop:
|
|||
dec cx
|
||||
jnz LoopTop
|
||||
sub si,bx
|
||||
</pre><!-- END CODE SNIP //-->
|
||||
</pre>
|
||||
|
||||
<p><br /></p>
|
||||
|
||||
|
|
@ -125,10 +118,6 @@ LoopTop:
|
|||
|
||||
<div align="center">
|
||||
Graphics Programming Black Book © 2001 Michael Abrash
|
||||
</div><!-- all of the reference materials (books) have the footer and subfoot reveresed -->
|
||||
<!-- reference_subfoot = footer -->
|
||||
<!-- reference_footer = subfoot -->
|
||||
<!-- BEGIN SUB FOOTER -->
|
||||
<!-- END FOOTER -->
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
53
12-02.html
53
12-02.html
|
|
@ -10,16 +10,9 @@
|
|||
<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: Pushing the 486</title><!-- HEADER -->
|
||||
<!-- Empty Reference Subhead -->
|
||||
<!--ISBN=1576101746//-->
|
||||
<!--TITLE=Michael Abrash's Graphics Programming Black Book Special Edition//-->
|
||||
<!--AUTHOR=Michael Abrash//-->
|
||||
<!--PUBLISHER=The Coriolis Group, Inc.//-->
|
||||
<title>Michael Abrash's Graphics Programming Black Book Special Edition: Pushing the 486</title>
|
||||
<meta name="chapter" content="12" />
|
||||
<meta name="pages" content="237-241" />
|
||||
<!--UNASSIGNED1//-->
|
||||
<!--UNASSIGNED2//-->
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
|
@ -47,35 +40,35 @@
|
|||
|
||||
<p>In a key loop on the 486, 1 cycle can indeed matter.</p>
|
||||
|
||||
<h4 align="left"><a id="Heading6"></a>Calculate Memory Pointers Ahead of Time</h4>
|
||||
<h4 align="left" id="Heading6">Calculate Memory Pointers Ahead of Time</h4>
|
||||
|
||||
<p>Rule #2: Don’t use a register as a memory pointer during the next two cycles after loading it.</p>
|
||||
|
||||
<p>Intel states that if the destination of one instruction is used as the base addressing component of the next instruction, then a one-cycle penalty is imposed. This rule, unlike anything ever before seen in the x86 family, reflects the heavily pipelined nature of the 486. Apparently, the 486 starts each effective address calculation before the start of the instruction that will need it, as shown in Figure 12.1; this effectively makes the address calculation time vanish, because it happens while the preceding instruction executes.</p>
|
||||
|
||||
<p>Of course, the 486 <i>can’t</i> perform an effective address calculation for a target instruction ahead of time if one of the address components isn’t known until the instruction starts, and that’s exactly the case when the preceding instruction modifies one of the target instruction’s addressing registers. For example, in the code</p><!-- CODE SNIP //-->
|
||||
<p>Of course, the 486 <i>can’t</i> perform an effective address calculation for a target instruction ahead of time if one of the address components isn’t known until the instruction starts, and that’s exactly the case when the preceding instruction modifies one of the target instruction’s addressing registers. For example, in the code</p>
|
||||
<pre>
|
||||
MOV BX,OFFSET MemVar
|
||||
MOV AX,[BX]
|
||||
</pre><!-- END CODE SNIP //-->
|
||||
</pre>
|
||||
|
||||
<p>there’s no way that the 486 can calculate the address referenced by <b>MOV AX,[BX]</b> until <b>MOV BX,OFFSET MemVar</b> finishes, so pipelining that calculation ahead of time is not possible. A good workaround is rearranging your code so that at least one instruction lies between the loading of the memory pointer and its use. For example, postdecrementing, as in the following</p><!-- CODE SNIP //-->
|
||||
<p>there’s no way that the 486 can calculate the address referenced by <b>MOV AX,[BX]</b> until <b>MOV BX,OFFSET MemVar</b> finishes, so pipelining that calculation ahead of time is not possible. A good workaround is rearranging your code so that at least one instruction lies between the loading of the memory pointer and its use. For example, postdecrementing, as in the following</p>
|
||||
<pre>
|
||||
LoopTop:
|
||||
add ax,[si]
|
||||
add si,2
|
||||
dec cx
|
||||
jnz LoopTop
|
||||
</pre><!-- END CODE SNIP //-->
|
||||
</pre>
|
||||
|
||||
<p>is faster than preincrementing, as in:</p><!-- CODE SNIP //-->
|
||||
<p>is faster than preincrementing, as in:</p>
|
||||
<pre>
|
||||
LoopTop:
|
||||
add si,2
|
||||
add ax,[SI]
|
||||
dec cx
|
||||
jnz LoopTop
|
||||
</pre><!-- END CODE SNIP //-->
|
||||
</pre>
|
||||
|
||||
<p>Now that we understand what Intel means by this rule, let me make a very important comment: My observations indicate that for real-mode code, the documentation understates the extent of the penalty for interrupting the address calculation pipeline by loading a memory pointer just before it’s used.</p>
|
||||
|
||||
|
|
@ -91,46 +84,44 @@ LoopTop:
|
|||
|
||||
<p>Considering that <b>MOV</b> normally takes only one cycle total, that’s quite a loss. For example, the postdecrement loop shown above is 2 full cycles faster than the preincrement loop, resulting in a 29 percent improvement in the performance of the entire loop. But wait, there’s more. If a register is loaded 2 cycles (which generally means 2 instructions, but, because some 486 instructions take more than 1 cycle,</p>
|
||||
|
||||
<p><a id="Fig1"><!-- </A><A HREF="javascript:displayWindow('images/12-01.jpg',407,155 )"> --><img src="images/12-01.jpg" /><br />
|
||||
<!-- </A>
|
||||
<BR><A HREF="javascript:displayWindow('images/12-01.jpg',407,155)"> --><b>Figure 12.1</b></a> <i>One-cycle-ahead address pipelining.</i></p>
|
||||
<p><a id="Fig1"><img src="images/12-01.jpg" /><br />
|
||||
<b>Figure 12.1</b></a> <i>One-cycle-ahead address pipelining.</i></p>
|
||||
|
||||
<p>the 2 are not always equivalent) before it’s used to point to memory, 1 cycle is lost. Therefore, whereas this code</p><!-- CODE SNIP //-->
|
||||
<p>the 2 are not always equivalent) before it’s used to point to memory, 1 cycle is lost. Therefore, whereas this code</p>
|
||||
<pre>
|
||||
mov bx,offset MemVar
|
||||
mov ax,[bx]
|
||||
inc dx
|
||||
dec cx
|
||||
jnz LoopTop
|
||||
</pre><!-- END CODE SNIP //-->
|
||||
</pre>
|
||||
|
||||
<p>loses two cycles from interrupting the address calculation pipeline, this code</p><!-- CODE SNIP //-->
|
||||
<p>loses two cycles from interrupting the address calculation pipeline, this code</p>
|
||||
<pre>
|
||||
mov bx,offset MemVar
|
||||
inc dx
|
||||
mov ax,[bx]
|
||||
dec cx
|
||||
jnz LoopTop
|
||||
</pre><!-- END CODE SNIP //-->
|
||||
</pre>
|
||||
|
||||
<p>loses only one cycle, and this code</p><!-- CODE SNIP //-->
|
||||
<p>loses only one cycle, and this code</p>
|
||||
<pre>
|
||||
mov bx,offset MemVar
|
||||
inc dx
|
||||
dec cx
|
||||
mov ax,[bx]
|
||||
jnz LoopTop
|
||||
</pre><!-- END CODE SNIP //-->
|
||||
</pre>
|
||||
|
||||
<p>loses no cycles at all. Apparently, the 486’s addressing calculation pipeline actually starts 2 cycles ahead, as shown in Figure 12.2. (In truth, my best guess at the moment is that the addressing pipeline really does start only 1 cycle ahead; the additional cycle crops up when the addressing pipeline has to wait for a register to be written into the register file before it can read it out for use in addressing calculations. However, I’m guessing here, and the 2-cycle-ahead model in Figure 12.2 will do just fine for optimization purposes.)</p>
|
||||
|
||||
<p>Clearly, there’s considerable optimization potential in careful rearrangement of 486 code.</p>
|
||||
|
||||
<p><a id="Fig2"><!-- </A><A HREF="javascript:displayWindow('images/12-02.jpg',411,158 )"> --><img src="images/12-02.jpg" /><br />
|
||||
<!-- </A>
|
||||
<BR><A HREF="javascript:displayWindow('images/12-02.jpg',411,158)"> --><b>Figure 12.2</b></a> <i>Two-cycle-ahead address pipelining.</i></p>
|
||||
<p><a id="Fig2"><img src="images/12-02.jpg" /><br />
|
||||
<b>Figure 12.2</b></a> <i>Two-cycle-ahead address pipelining.</i></p>
|
||||
|
||||
<h3><a id="Heading7"></a>Caveat Programmor</h3>
|
||||
<h3 id="Heading7">Caveat Programmor</h3>
|
||||
|
||||
<p>A caution: I’m quite certain that the 2-cycle-ahead addressing pipeline interruption penalty I’ve described exists in the two 486s I’ve tested. However, there’s no guarantee that Intel won’t change this aspect of the 486 in the future, especially given that the documentation indicates otherwise. Perhaps the 2-cycle penalty is the result of a bug in the initial steps of the 486, and will revert to the documented 1-cycle penalty someday; likewise for the undocumented optimizations I’ll describe below. Nonetheless, none of the optimizations I suggest would hurt performance even if the undocumented performance characteristics of the 486 were to vanish, and they certainly will help performance on at least some 486s right now, so I feel they’re well worth using.</p>
|
||||
|
||||
|
|
@ -151,10 +142,6 @@ jnz LoopTop
|
|||
|
||||
<div align="center">
|
||||
Graphics Programming Black Book © 2001 Michael Abrash
|
||||
</div><!-- all of the reference materials (books) have the footer and subfoot reveresed -->
|
||||
<!-- reference_subfoot = footer -->
|
||||
<!-- reference_footer = subfoot -->
|
||||
<!-- BEGIN SUB FOOTER -->
|
||||
<!-- END FOOTER -->
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
47
12-03.html
47
12-03.html
|
|
@ -10,16 +10,9 @@
|
|||
<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: Pushing the 486</title><!-- HEADER -->
|
||||
<!-- Empty Reference Subhead -->
|
||||
<!--ISBN=1576101746//-->
|
||||
<!--TITLE=Michael Abrash's Graphics Programming Black Book Special Edition//-->
|
||||
<!--AUTHOR=Michael Abrash//-->
|
||||
<!--PUBLISHER=The Coriolis Group, Inc.//-->
|
||||
<title>Michael Abrash's Graphics Programming Black Book Special Edition: Pushing the 486</title>
|
||||
<meta name="chapter" content="12" />
|
||||
<meta name="pages" content="241-243" />
|
||||
<!--UNASSIGNED1//-->
|
||||
<!--UNASSIGNED2//-->
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
|
@ -39,13 +32,13 @@
|
|||
|
||||
<p>There is, of course, no guarantee that I’m entirely correct about the optimizations discussed in this chapter. Without knowing the internals of the 486, all I can do is time code and make inferences from the results; I invite you to deduce your own rules and cross-check them against mine. Also, most likely there are other optimizations that I’m unaware of. If you have further information on these or any other undocumented optimizations, please write and let me know. And, of course, if anyone from Intel is reading this and wants to give us the gospel truth, please do!</p>
|
||||
|
||||
<h4 align="left"><a id="Heading8"></a>Stack Addressing and Address Pipelining</h4>
|
||||
<h4 align="left" id="Heading8">Stack Addressing and Address Pipelining</h4>
|
||||
|
||||
<p>Rule #2A: Rule #2 sometimes, but not always, applies to the stack pointer when it is implicitly used to point to memory.</p>
|
||||
|
||||
<p>Intel states that the stack pointer is an implied destination register for <b>CALL</b>, <b>ENTER</b>, <b>LEAVE</b>, <b>RET</b>, <b>PUSH</b>, and <b>POP</b> (which alter (E)SP), and that it is the implied base addressing register for <b>PUSH</b>, <b>POP</b>, and <b>RET</b> (which use (E)SP to address memory). Intel then implies that the aforementioned addressing pipeline penalty is incurred whenever the stack pointer is used as a destination by one of the first set of instructions and is then immediately used to address memory by one of the second set. This raises the specter of unpleasant programming contortions such as intermixing <b>PUSH</b>es and <b>POP</b>s with other instructions to avoid interrupting the addressing pipeline. Fortunately, matters are actually not so grim as Intel’s documentation would indicate; my tests indicate that the addressing pipeline penalty pops up only spottily when the stack pointer is involved.</p>
|
||||
|
||||
<p>For example, you’d certainly expect a sequence such as</p><!-- CODE SNIP //-->
|
||||
<p>For example, you’d certainly expect a sequence such as</p>
|
||||
<pre>
|
||||
:
|
||||
pop ax
|
||||
|
|
@ -53,67 +46,67 @@ ret
|
|||
pop ax
|
||||
et
|
||||
:
|
||||
</pre><!-- END CODE SNIP //-->
|
||||
</pre>
|
||||
|
||||
<p>to exhibit the addressing pipeline interruption phenomenon (SP is both destination and addressing register for both instructions, according to Intel), but this code runs in six cycles per <b>POP/RET</b> pair, matching the official execution times exactly. Likewise, a sequence like</p><!-- CODE SNIP //-->
|
||||
<p>to exhibit the addressing pipeline interruption phenomenon (SP is both destination and addressing register for both instructions, according to Intel), but this code runs in six cycles per <b>POP/RET</b> pair, matching the official execution times exactly. Likewise, a sequence like</p>
|
||||
<pre>
|
||||
pop dx
|
||||
pop cx
|
||||
pop bx
|
||||
pop ax
|
||||
</pre><!-- END CODE SNIP //-->
|
||||
</pre>
|
||||
|
||||
<p>runs in one cycle per instruction, just as it should.</p>
|
||||
|
||||
<p>On the other hand, performing arithmetic directly on SP as an <i>explicit</i> destination—for example, to deallocate local variables—and then using <b>PUSH</b>, <b>POP</b>, or <b>RET</b>, definitely can interrupt the addressing pipeline. For example</p><!-- CODE SNIP //-->
|
||||
<p>On the other hand, performing arithmetic directly on SP as an <i>explicit</i> destination—for example, to deallocate local variables—and then using <b>PUSH</b>, <b>POP</b>, or <b>RET</b>, definitely can interrupt the addressing pipeline. For example</p>
|
||||
<pre>
|
||||
add sp,10h
|
||||
ret
|
||||
</pre><!-- END CODE SNIP //-->
|
||||
</pre>
|
||||
|
||||
<p>loses two cycles because SP is the explicit destination of one instruction and then the implied addressing register for the next, and the sequence</p><!-- CODE SNIP //-->
|
||||
<p>loses two cycles because SP is the explicit destination of one instruction and then the implied addressing register for the next, and the sequence</p>
|
||||
<pre>
|
||||
add sp,10h
|
||||
pop ax
|
||||
</pre><!-- END CODE SNIP //-->
|
||||
</pre>
|
||||
|
||||
<p>loses two cycles for the same reason.</p>
|
||||
|
||||
<p>I certainly haven’t tried all possible combinations, but the results so far indicate that the stack pointer incurs the addressing pipeline penalty only if (E)SP is the <i>explicit</i> destination of one instruction and is then used by one of the two following instructions to address memory. So, for instance, SP isn’t the explicit operand of <b>POP AX—</b>AX is—and no cycles are lost if <b>POP AX</b> is followed by <b>POP</b> or <b>RET</b>. Happily, then, we need not worry about the sequence in which we use <b>PUSH</b> and <b>POP</b>. However, adding to, moving to, or subtracting from the stack pointer should ideally be done at least two cycles before <b>PUSH</b>, <b>POP</b>, <b>RET</b>, or any other instruction that uses the stack pointer to address memory.</p>
|
||||
|
||||
<h4 align="left"><a id="Heading9"></a>Problems with Byte Registers</h4>
|
||||
<h4 align="left" id="Heading9">Problems with Byte Registers</h4>
|
||||
|
||||
<p>There are two ways to lose cycles by using byte registers, and neither of them is documented by Intel, so far as I know. Let’s start with the lesser and simpler of the two.</p>
|
||||
|
||||
<p>Rule #3: Do not load a byte portion of a register during one instruction, then use that register in its entirety as a source register during the next instruction.</p>
|
||||
|
||||
<p>So, for example, it would be a bad idea to do this</p><!-- CODE SNIP //-->
|
||||
<p>So, for example, it would be a bad idea to do this</p>
|
||||
<pre>
|
||||
mov ah,o
|
||||
:
|
||||
mov cx,[MemVar1]
|
||||
mov al,[MemVar2]
|
||||
add cx,ax
|
||||
</pre><!-- END CODE SNIP //-->
|
||||
</pre>
|
||||
|
||||
<p>because AL is loaded by one instruction, then AX is used as the source register for the next instruction. A cycle can be saved simply by rearranging the instructions so that the byte register load isn’t immediately followed by the word register usage, like so:</p><!-- CODE SNIP //-->
|
||||
<p>because AL is loaded by one instruction, then AX is used as the source register for the next instruction. A cycle can be saved simply by rearranging the instructions so that the byte register load isn’t immediately followed by the word register usage, like so:</p>
|
||||
<pre>
|
||||
mov ah,o
|
||||
:
|
||||
mov al,[MemVar2]
|
||||
mov cx,[MemVar1]
|
||||
add cx,ax
|
||||
</pre><!-- END CODE SNIP //-->
|
||||
</pre>
|
||||
|
||||
<p>Strange as it may seem, this rule is neither arbitrary nor nonsensical. Basically, when a byte destination register is part of a word source register for the next instruction, the 486 is unable to directly use the result from the first instruction as the source for the second instruction, because only part of the register required by the second instruction is contained in the first instruction’s result. The full, updated register value must be read from the register file, and that value can’t be read out until the result from the first instruction has been written <i>into</i> the register file, a process that takes an extra cycle. I’m not going to explain this in great detail because it’s not important that you understand why this rule exists (only that it <i>does</i> in fact exist), but it is an interesting window on the way the 486 works.</p>
|
||||
|
||||
<p>In case you’re curious, there’s no such penalty for the typical <b>XLAT</b> sequence like</p><!-- CODE SNIP //-->
|
||||
<p>In case you’re curious, there’s no such penalty for the typical <b>XLAT</b> sequence like</p>
|
||||
<pre>
|
||||
mov bx,offset MemTable
|
||||
:
|
||||
mov al,[si]
|
||||
xlat
|
||||
</pre><!-- END CODE SNIP //-->
|
||||
</pre>
|
||||
|
||||
<p>even though AL must be converted to a word by <b>XLAT</b> before it can be added to BX and used to address memory. In fact, none of the penalties mentioned in this chapter apply to <b>XLAT</b>, apparently because <b>XLAT</b> is so slow—4 cycles—that it gives the 486 time to perform addressing calculations during the course of the instruction.</p>
|
||||
|
||||
|
|
@ -144,10 +137,6 @@ xlat
|
|||
|
||||
<div align="center">
|
||||
Graphics Programming Black Book © 2001 Michael Abrash
|
||||
</div><!-- all of the reference materials (books) have the footer and subfoot reveresed -->
|
||||
<!-- reference_subfoot = footer -->
|
||||
<!-- reference_footer = subfoot -->
|
||||
<!-- BEGIN SUB FOOTER -->
|
||||
<!-- END FOOTER -->
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
45
12-04.html
45
12-04.html
|
|
@ -10,16 +10,9 @@
|
|||
<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: Pushing the 486</title><!-- HEADER -->
|
||||
<!-- Empty Reference Subhead -->
|
||||
<!--ISBN=1576101746//-->
|
||||
<!--TITLE=Michael Abrash's Graphics Programming Black Book Special Edition//-->
|
||||
<!--AUTHOR=Michael Abrash//-->
|
||||
<!--PUBLISHER=The Coriolis Group, Inc.//-->
|
||||
<title>Michael Abrash's Graphics Programming Black Book Special Edition: Pushing the 486</title>
|
||||
<meta name="chapter" content="12" />
|
||||
<meta name="pages" content="243-246" />
|
||||
<!--UNASSIGNED1//-->
|
||||
<!--UNASSIGNED2//-->
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
|
@ -39,41 +32,41 @@
|
|||
|
||||
<p>You don’t need to understand every corner of the 486 universe unless you’re a diehard ASMhead who does this stuff for fun. Just learn enough to be able to speed up the key portions of your programs, and spend the rest of your time on a fast design and overall implementation.</p>
|
||||
|
||||
<h4 align="left"><a id="Heading10"></a>More Fun with Byte Registers</h4>
|
||||
<h4 align="left" id="Heading10">More Fun with Byte Registers</h4>
|
||||
|
||||
<p>Rule #4: Don’t load <i>any</i> byte register exactly 2 cycles before using <i>any</i> register to address memory.</p>
|
||||
|
||||
<p>This, the last of this chapter’s rules, is the strangest of the lot. If any byte register is loaded, and then two cycles later any register is used to point to memory, one cycle is lost. So, for example, this code</p><!-- CODE SNIP //-->
|
||||
<p>This, the last of this chapter’s rules, is the strangest of the lot. If any byte register is loaded, and then two cycles later any register is used to point to memory, one cycle is lost. So, for example, this code</p>
|
||||
<pre>
|
||||
mov al,bl
|
||||
mov cx,dx
|
||||
mov si,[di]
|
||||
</pre><!-- END CODE SNIP //-->
|
||||
</pre>
|
||||
|
||||
<p>takes four rather than the expected three cycles to execute. Note that it is <i>not</i> required that the byte register be part of the register used to address memory; any byte register will do the trick.</p>
|
||||
|
||||
<p>Worse still, loading byte registers both one and two cycles before a register is used to address memory costs two cycles, as in</p><!-- CODE SNIP //-->
|
||||
<p>Worse still, loading byte registers both one and two cycles before a register is used to address memory costs two cycles, as in</p>
|
||||
<pre>
|
||||
mov bl,al
|
||||
mov cl,3
|
||||
mov bx,[si]
|
||||
</pre><!-- END CODE SNIP //-->
|
||||
</pre>
|
||||
|
||||
<p>which takes five rather than three cycles to run. However, there is <i>no</i> penalty if a byte register is loaded one cycle but not two cycles before a register is used to address memory. Therefore,</p><!-- CODE SNIP //-->
|
||||
<p>which takes five rather than three cycles to run. However, there is <i>no</i> penalty if a byte register is loaded one cycle but not two cycles before a register is used to address memory. Therefore,</p>
|
||||
<pre>
|
||||
mov cx,3
|
||||
mov dl,al
|
||||
mov si,[bx]
|
||||
</pre><!-- END CODE SNIP //-->
|
||||
</pre>
|
||||
|
||||
<p>runs in the expected three cycles.</p>
|
||||
|
||||
<p>In truth, I do not know why this happens. Clearly, it has something to do with interrupting the start of the addressing pipeline, and I have my theories about how this works, but at this point they’re pure speculation. Whatever the reason for this rule, ignorance of it—and of its interaction with the other rules—could lead to considerable performance loss in seemingly air-tight code. For instance, a casual observer would expect the following code to run in 3 cycles:</p><!-- CODE SNIP //-->
|
||||
<p>In truth, I do not know why this happens. Clearly, it has something to do with interrupting the start of the addressing pipeline, and I have my theories about how this works, but at this point they’re pure speculation. Whatever the reason for this rule, ignorance of it—and of its interaction with the other rules—could lead to considerable performance loss in seemingly air-tight code. For instance, a casual observer would expect the following code to run in 3 cycles:</p>
|
||||
<pre>
|
||||
mov bx,offset MemVar
|
||||
mov cl,al
|
||||
mov ax,[bx]
|
||||
</pre><!-- END CODE SNIP //-->
|
||||
</pre>
|
||||
|
||||
<p>A more sophisticated programmer would expect to lose one cycle, because BX is loaded two cycles before being used to address memory. In fact, though, this code takes 5 cycles—2 cycles, or 67 percent, longer than normal. Why? Well, under normal conditions, loading a byte register—CL in this case—one cycle before using a register to address memory produces no penalty; loading 2 cycles ahead is the only case that normally incurs a penalty. However, think of Rule #4 as meaning that loading a byte register disrupts the memory addressing pipeline as it starts up. Viewed that way, we can see that <b>MOV BX,OFFSET MemVar</b> interrupts the addressing pipeline, forcing it to start again, and then, presumably, <b>MOV CL,AL</b> interrupts the pipeline again because the pipeline is now on its first cycle: the one that loading a byte register can affect.</p>
|
||||
|
||||
|
|
@ -85,11 +78,11 @@ mov ax,[bx]
|
|||
</tr>
|
||||
</table>
|
||||
|
||||
<h4 align="left"><a id="Heading11"></a>Timing Your Own 486 Code</h4>
|
||||
<h4 align="left" id="Heading11">Timing Your Own 486 Code</h4>
|
||||
|
||||
<p>In case you want to do some 486 performance analysis of your own, let me show you how I arrived at one of the above conclusions; at the same time, I can warn you of the timing hazards of the cache. Listings 12.1 and 12.2 show the code I ran through the Zen timer in order to establish the effects of loading a byte register before using a register to address memory. Listing 12.1 ran in 120 µs on a 33 MHz 486, or 4 cycles per repetition (120 µs/1000 repetitions = 120 ns per repetition; 120 ns per repetition/30 ns per cycle = 4 cycles per repetition); Listing 12.2 ran in 90 µs, or 3 cycles, establishing that loading a byte register costs a cycle only when it’s performed exactly 2 cycles before addressing memory.</p>
|
||||
|
||||
<p><b>LISTING 12.1 LST12-1.ASM</b></p><!-- CODE //-->
|
||||
<p><b>LISTING 12.1 LST12-1.ASM</b></p>
|
||||
<pre>
|
||||
; Measures the effect of loading a byte register 2 cycles before
|
||||
; using a register to address memory.
|
||||
|
|
@ -108,9 +101,9 @@ CacheFillLoop:
|
|||
jz Done
|
||||
jmp CacheFillLoop
|
||||
Done:
|
||||
</pre><!-- END CODE //-->
|
||||
</pre>
|
||||
|
||||
<p><b>LISTING 12.2 LST12-2.ASM</b></p><!-- CODE //-->
|
||||
<p><b>LISTING 12.2 LST12-2.ASM</b></p>
|
||||
<pre>
|
||||
; Measures the effect of loading a byte register 1 cycle before
|
||||
; using a register to address memory.
|
||||
|
|
@ -129,7 +122,7 @@ CacheFillLoop:
|
|||
jz Done
|
||||
jmp CacheFillLoop
|
||||
Done:
|
||||
</pre><!-- END CODE //-->
|
||||
</pre>
|
||||
|
||||
<p>Note that Listings 12.1 and 12.2 each repeat the timing of the code under test a second time, to make sure that the instructions are in the cache on the second pass, the one for which results are displayed. Also note that the code is less than 8K in size, so that it can all fit in the 486’s 8K internal cache. If I double the <b>REPT</b> value in Listing 12.2 to 2,000, making the test code larger than 8K, the execution time more than doubles to 224 µs, or 3.7 cycles per repetition; the extra seven-tenths of a cycle comes from fetching non-cached instruction bytes.</p>
|
||||
|
||||
|
|
@ -141,7 +134,7 @@ Done:
|
|||
</tr>
|
||||
</table>
|
||||
|
||||
<h3><a id="Heading12"></a>The Story Continues</h3>
|
||||
<h3 id="Heading12">The Story Continues</h3>
|
||||
|
||||
<p>There’s certainly plenty more 486 lore to explore, including the 486’s unique prefetch queue, more optimization rules, branching optimizations, performance implications of the cache, the cost of cache misses for reads, and the implications of cache write-through for writes. Nonetheless, we’ve covered quite a bit of ground in this chapter, and I trust you’ve gotten a feel for the considerable extent to which 486 optimization differs from what you’re used to. Odd as 486 optimization is, though, it’s well worth mastering, for the 486 is, at its best, so staggeringly fast that carefully crafted 486 code can do more than twice as much per cycle as the best 386 code—which makes it perhaps 50 times as fast as optimized code for the original PC.</p>
|
||||
|
||||
|
|
@ -164,10 +157,6 @@ Done:
|
|||
|
||||
<div align="center">
|
||||
Graphics Programming Black Book © 2001 Michael Abrash
|
||||
</div><!-- all of the reference materials (books) have the footer and subfoot reveresed -->
|
||||
<!-- reference_subfoot = footer -->
|
||||
<!-- reference_footer = subfoot -->
|
||||
<!-- BEGIN SUB FOOTER -->
|
||||
<!-- END FOOTER -->
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
34
13-01.html
34
13-01.html
|
|
@ -10,16 +10,9 @@
|
|||
<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: Aiming the 486</title><!-- HEADER -->
|
||||
<!-- Empty Reference Subhead -->
|
||||
<!--ISBN=1576101746//-->
|
||||
<!--TITLE=Michael Abrash's Graphics Programming Black Book Special Edition//-->
|
||||
<!--AUTHOR=Michael Abrash//-->
|
||||
<!--PUBLISHER=The Coriolis Group, Inc.//-->
|
||||
<title>Michael Abrash's Graphics Programming Black Book Special Edition: Aiming the 486</title>
|
||||
<meta name="chapter" content="13" />
|
||||
<meta name="pages" content="248-251" />
|
||||
<!--UNASSIGNED1//-->
|
||||
<!--UNASSIGNED2//-->
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
|
@ -37,10 +30,10 @@
|
|||
|
||||
<p><br /></p>
|
||||
|
||||
<h2><a id="Heading1"></a>Chapter 13<br />
|
||||
<h2 id="Heading1">Chapter 13<br />
|
||||
Aiming the 486</h2>
|
||||
|
||||
<h3><a id="Heading2"></a>Pipelines and Other Hazards of the High End</h3>
|
||||
<h3 id="Heading2">Pipelines and Other Hazards of the High End</h3>
|
||||
|
||||
<p>It’s a sad but true fact that 84 percent of American schoolchildren are ignorant of 92 percent of American history. Not my daughter, though. We recently visited historical Revolutionary-War-vintage Fort Ticonderoga, and she’s now 97 percent aware of a key element of our national heritage: that the basic uniform for soldiers in those days was what appears to be underwear, plus a hat so that no one could complain that they were undermining family values. Ha! Just kidding! Actually, what she learned was that in those days, it was pure coincidence if a cannonball actually hit anything it was aimed at, which isn’t surprising considering the lack of rifling, precision parts, and ballistics. The guides at the fort shot off three cannons; the closest they came to the target was about 50 feet, and that was only because the wind helped. I think the idea in early wars was just to put so much lead in the air that some of it was bound to hit <i>something;</i> preferably, but not necessarily, the enemy.</p>
|
||||
|
||||
|
|
@ -50,27 +43,26 @@
|
|||
|
||||
<p>For example, consider how Terje Mathisen doubled the speed of his word-counting program on a 486 simply by shuffling a couple of instructions.</p>
|
||||
|
||||
<h4 align="left"><a id="Heading3"></a>486 Pipeline Optimization</h4>
|
||||
<h4 align="left" id="Heading3">486 Pipeline Optimization</h4>
|
||||
|
||||
<p>I’ve mentioned Terje Mathisen in my writings before. Terje is an assembly language programmer extraordinaire, and author of the incredibly fast public-domain word-counting program WC (which comes complete with source code; well worth a look, if you want to see what <i>really</i> fast code looks like). Terje’s a regular participant in the ibm.pc/fast.code topic on Bix. In a thread titled “486 Pipeline Optimization, or TANSTATFC (There Ain’t No Such Thing As The Fastest Code),” he detailed the following optimization to WC, perhaps the best example of 486 pipeline optimization I’ve yet seen.</p>
|
||||
|
||||
<p>Terje’s inner loop originally looked something like the code in Listing 13.1. (I’ve taken a few liberties for illustrative purposes.) Of course, Terje unrolls this loop a few times (128 times, to be exact). By the way, in Listing 13.1 you’ll notice that Terje counts not only words but also lines, at a rate of three instructions for every two characters!</p>
|
||||
|
||||
<p><b>LISTING 13.1 L13-1.ASM</b></p><!-- CODE SNIP //-->
|
||||
<p><b>LISTING 13.1 L13-1.ASM</b></p>
|
||||
<pre>
|
||||
mov di,[bp+OFFS] ;get the next pair of characters
|
||||
mov bl,[di] ;get the state value for the pair
|
||||
add dx,[bx+8000h] ;increment word and line count
|
||||
; appropriately for the pair
|
||||
</pre><!-- END CODE SNIP //-->
|
||||
</pre>
|
||||
|
||||
<p>Listing 13.1 looks as tight as it could be, with just two one-cycle instructions, one two-cycle instruction, and no branches. It <i>is</i> tight, but those three instructions actually take a minimum of 8 cycles to execute, as shown in Figure 13.1. The problem is that DI is loaded just before being used to address memory, and that costs 2 cycles because it interrupts the 486’s internal instruction pipeline. Likewise, BX is loaded just before being used to address memory, costing another two cycles. Thus, this loop takes twice as long as cycle counts would seem to indicate, simply because two registers are loaded immediately before being used, disrupting the 486’s pipeline.</p>
|
||||
|
||||
<p>Listing 13.2 shows Terje’s immediate response to these pipelining problems; he simply swapped the instructions that load DI and BL. This one change cut execution time per character pair from eight cycles to five cycles! The load of BL is now separated by one instruction from the use of BX to address memory, so the pipeline penalty is reduced from two cycles to one cycle. The load of DI is also separated by one instruction from the use of DI to address memory (remember, the loop is unrolled, so the last instruction is followed by the first instruction), but because the intervening instruction takes two cycles, there’s no penalty at all.</p>
|
||||
|
||||
<p><a id="Fig1"><!-- </A><A HREF="javascript:displayWindow('images/13-01.jpg',100,65 )"> --><img src="images/13-01.jpg" /><br />
|
||||
<!-- </A>
|
||||
<BR><A HREF="javascript:displayWindow('images/13-01.jpg',100,65)"> --><b>Figure 13.1</b></a> <i>Cycle-eaters in the original WC.</i></p>
|
||||
<p><a id="Fig1"><img src="images/13-01.jpg" /><br />
|
||||
<b>Figure 13.1</b></a> <i>Cycle-eaters in the original WC.</i></p>
|
||||
|
||||
<table width="100%">
|
||||
<tr>
|
||||
|
|
@ -80,13 +72,13 @@ add dx,[bx+8000h] ;increment word and line count
|
|||
</tr>
|
||||
</table>
|
||||
|
||||
<p><b>LISTING 13.2 L13-2.ASM</b></p><!-- CODE SNIP //-->
|
||||
<p><b>LISTING 13.2 L13-2.ASM</b></p>
|
||||
<pre>
|
||||
mov bl,[di] ;get the state value for the pair
|
||||
mov di,[bp+OFFS] ;get the next pair of characters
|
||||
add dx,[bx+8000h] ;increment word and line count
|
||||
; appropriately for the pair
|
||||
</pre><!-- END CODE SNIP //-->
|
||||
</pre>
|
||||
|
||||
<p>At this point, Terje had nearly doubled the performance of this code simply by moving one instruction. (Note that swapping the instructions also made it necessary to preload DI at the start of the loop; Listing 13.2 is not exactly equivalent to Listing 13.1.) I’ll let Terje describe his next optimization in his own words:</p>
|
||||
|
||||
|
|
@ -107,10 +99,6 @@ add dx,[bx+8000h] ;increment word and line count
|
|||
|
||||
<div align="center">
|
||||
Graphics Programming Black Book © 2001 Michael Abrash
|
||||
</div><!-- all of the reference materials (books) have the footer and subfoot reveresed -->
|
||||
<!-- reference_subfoot = footer -->
|
||||
<!-- reference_footer = subfoot -->
|
||||
<!-- BEGIN SUB FOOTER -->
|
||||
<!-- END FOOTER -->
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
38
13-02.html
38
13-02.html
|
|
@ -10,16 +10,9 @@
|
|||
<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: Aiming the 486</title><!-- HEADER -->
|
||||
<!-- Empty Reference Subhead -->
|
||||
<!--ISBN=1576101746//-->
|
||||
<!--TITLE=Michael Abrash's Graphics Programming Black Book Special Edition//-->
|
||||
<!--AUTHOR=Michael Abrash//-->
|
||||
<!--PUBLISHER=The Coriolis Group, Inc.//-->
|
||||
<title>Michael Abrash's Graphics Programming Black Book Special Edition: Aiming the 486</title>
|
||||
<meta name="chapter" content="13" />
|
||||
<meta name="pages" content="252-254" />
|
||||
<!--UNASSIGNED1//-->
|
||||
<!--UNASSIGNED2//-->
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
|
@ -39,47 +32,46 @@
|
|||
|
||||
<p>“When I looked closely as this, I realized that the two cycles for the final <b>ADD</b> is just the sum of 1 cycle to load the data from memory, and 1 cycle to add it to DX, so the code could just as well have been written as shown in Listing 13.3. The final breakthrough came when I realized that by initializing AX to zero outside the loop, I could rearrange it as shown in Listing 13.4 and do the final <b>ADD DX,AX</b> after the loop. This way there are two single-cycle instructions between the first and the fourth line, avoiding all pipeline stalls, for a total throughput of two cycles/char.”</p>
|
||||
|
||||
<p><b>LISTING 13.3 L13-3.ASM</b></p><!-- CODE SNIP //-->
|
||||
<p><b>LISTING 13.3 L13-3.ASM</b></p>
|
||||
<pre>
|
||||
mov bl,[di] ;get the state value for the pair
|
||||
mov di,[bp+OFFS] ;get the next pair of characters
|
||||
mov ax,[bx+8000h] ;increment word and line count
|
||||
add dx,ax ; appropriately for the pair
|
||||
</pre><!-- END CODE SNIP //-->
|
||||
</pre>
|
||||
|
||||
<p><b>LISTING 13.4 L13-4.ASM</b></p><!-- CODE SNIP //-->
|
||||
<p><b>LISTING 13.4 L13-4.ASM</b></p>
|
||||
<pre>
|
||||
mov bl,[di] ;get the state value for the pair
|
||||
mov di,[bp+OFFS] ;get the next pair of characters
|
||||
add dx,ax ;increment word and line count
|
||||
; appropriately for the pair
|
||||
mov ax,[bx+8000h] ;get increments for next time
|
||||
</pre><!-- END CODE SNIP //-->
|
||||
</pre>
|
||||
|
||||
<p>I’d like to point out two fairly remarkable things. First, the single cycle that Terje saved in Listing 13.4 sped up his entire word-counting engine by 25 percent or more; Listing 13.4 is fully twice as fast as Listing 13.1—all the result of nothing more than shifting an instruction and splitting another into two operations. Second, Terje’s word-counting engine can process more than 16 million characters <i>per second</i> on a 486/33.</p>
|
||||
|
||||
<p>Clever 486 optimization can pay off big. QED.</p>
|
||||
|
||||
<h3><a id="Heading4"></a>BSWAP: More Useful Than You Might Think</h3>
|
||||
<h3 id="Heading4">BSWAP: More Useful Than You Might Think</h3>
|
||||
|
||||
<p>There are only 3 non-system instructions unique to the 486. None is earthshaking, but they have their uses. Consider <b>BSWAP. BSWAP</b> does just what its name implies, swapping the bytes (not bits) of a 32-bit register from one end of the register to the other, as shown in Figure 13.2. (<b>BSWAP</b> can only work with 32-bit registers; memory locations and 16-bit registers are not valid operands.) The obvious use of <b>BSWAP</b> is to convert data from Intel format (least significant byte first in memory, also called <i>little endian</i>) to Motorola format (most significant byte first in memory, or <i>big endian</i>), like so:</p><!-- CODE SNIP //-->
|
||||
<p>There are only 3 non-system instructions unique to the 486. None is earthshaking, but they have their uses. Consider <b>BSWAP. BSWAP</b> does just what its name implies, swapping the bytes (not bits) of a 32-bit register from one end of the register to the other, as shown in Figure 13.2. (<b>BSWAP</b> can only work with 32-bit registers; memory locations and 16-bit registers are not valid operands.) The obvious use of <b>BSWAP</b> is to convert data from Intel format (least significant byte first in memory, also called <i>little endian</i>) to Motorola format (most significant byte first in memory, or <i>big endian</i>), like so:</p>
|
||||
<pre>
|
||||
lodsd
|
||||
bswap
|
||||
stosd
|
||||
</pre><!-- END CODE SNIP //-->
|
||||
</pre>
|
||||
|
||||
<p><b>BSWAP</b> can also be useful for reversing the order of pixel bits from a bitmap so that they can be rotated 32 bits at a time with an instruction such as <b>ROR EAX,1</b>. Intel’s byte ordering for multiword values (least-significant byte first) loads pixels in the wrong order, so far as word rotation is concerned, but <b>BSWAP</b> can take care of that.</p>
|
||||
|
||||
<p><a id="Fig2"><!-- </A><A HREF="javascript:displayWindow('images/13-02.jpg',409,245 )"> --><img src="images/13-02.jpg" /><br />
|
||||
<!-- </A>
|
||||
<BR><A HREF="javascript:displayWindow('images/13-02.jpg',409,245)"> --><b>Figure 13.2</b></a> <i>BSWAP in operation.</i></p>
|
||||
<p><a id="Fig2"><img src="images/13-02.jpg" /><br />
|
||||
<b>Figure 13.2</b></a> <i>BSWAP in operation.</i></p>
|
||||
|
||||
<p>As it turns out, though, <b>BSWAP</b> is also useful in an unexpected way, having to do with making efficient use of the upper half of 32-bit registers. As any assembly language programmer knows, the x86 register set is too small; or, to phrase that another way, it sure would be nice if the register set were bigger. As any 386/486 assembly language programmer knows, there are many cases in which 16 bits is plenty. For example, a 16-bit scan-line counter generally does the trick nicely in a video driver, because there are <i>very</i> few video devices with more than 65,535 addressable scan lines. Combining these two observations yields the obvious conclusion that it would be great if there were some way to use the upper and lower 16 bits of selected 386 registers as separate 16-bit registers, effectively increasing the available register space.</p>
|
||||
|
||||
<p>Unfortunately, the x86 instruction set doesn’t provide any way to work directly with only the upper half of a 32-bit register. The next best solution is to rotate the register to give you access in the lower 16 bits to the half you need at any particular time, with code along the lines of that in Listing 13.5. Having to rotate the 16-bit fields into position certainly isn’t as good as having direct access to the upper half, but surely it’s better than having to get the values out of memory, isn’t it?</p>
|
||||
|
||||
<p><b>LISTING 13.5 L13-5.ASM</b></p><!-- CODE //-->
|
||||
<p><b>LISTING 13.5 L13-5.ASM</b></p>
|
||||
<pre>
|
||||
mov cx,[initialskip]
|
||||
shl ecx,16 ;put skip value in upper half of ECX
|
||||
|
|
@ -92,7 +84,7 @@ looptop:
|
|||
ror ecx,16 ;put loop count in CX
|
||||
dec cx ;count down loop
|
||||
jnz looptop
|
||||
</pre><!-- END CODE //-->
|
||||
</pre>
|
||||
|
||||
<p>Not necessarily. Shifts and rotates are among the worst performing instructions of the 486, taking 2 to 3 cycles to execute. Thus, it takes 2 cycles to rotate the skip value into CX in Listing 13.5, and 2 more cycles to rotate it back to the upper half of ECX. I’d say four cycles is a pretty steep price to pay, especially considering that a <b>MOV</b> to or from memory takes only one cycle. Basically, using <b>ROR</b> to access a 16-bit value in the upper half of a 16-bit register is a pretty marginal technique, unless for some reason you can’t access memory at all (for example, if you’re using BP as a working register, temporarily making the stack frame inaccessible).</p>
|
||||
|
||||
|
|
@ -113,10 +105,6 @@ looptop:
|
|||
|
||||
<div align="center">
|
||||
Graphics Programming Black Book © 2001 Michael Abrash
|
||||
</div><!-- all of the reference materials (books) have the footer and subfoot reveresed -->
|
||||
<!-- reference_subfoot = footer -->
|
||||
<!-- reference_footer = subfoot -->
|
||||
<!-- BEGIN SUB FOOTER -->
|
||||
<!-- END FOOTER -->
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
35
13-03.html
35
13-03.html
|
|
@ -10,16 +10,9 @@
|
|||
<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: Aiming the 486</title><!-- HEADER -->
|
||||
<!-- Empty Reference Subhead -->
|
||||
<!--ISBN=1576101746//-->
|
||||
<!--TITLE=Michael Abrash's Graphics Programming Black Book Special Edition//-->
|
||||
<!--AUTHOR=Michael Abrash//-->
|
||||
<!--PUBLISHER=The Coriolis Group, Inc.//-->
|
||||
<title>Michael Abrash's Graphics Programming Black Book Special Edition: Aiming the 486</title>
|
||||
<meta name="chapter" content="13" />
|
||||
<meta name="pages" content="254-256" />
|
||||
<!--UNASSIGNED1//-->
|
||||
<!--UNASSIGNED2//-->
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
|
@ -39,7 +32,7 @@
|
|||
|
||||
<p>On the 386, <b>ROR</b> was the only way to split a 32-bit register into two 16-bit registers. On the 486, however, <b>BSWAP</b> can not only do the job, but can do it better, because <b>BSWAP</b> executes in just one cycle. <b>BSWAP</b> has the added benefit of not affecting any flags, unlike <b>ROR</b>. With <b>BSWAP</b>-based code like that in Listing 13.6, the upper 16 bits of a register can be accessed with only 2 cycles of overhead and without altering any flags, making the technique of packing two 16-bit registers into one 32-bit register much more useful.</p>
|
||||
|
||||
<p><b>LISTING 13.6 L13-6.ASM</b></p><!-- CODE //-->
|
||||
<p><b>LISTING 13.6 L13-6.ASM</b></p>
|
||||
<pre>
|
||||
mov cx,[initialskip]
|
||||
bswap ecx ;put skip value in upper half of ECX
|
||||
|
|
@ -52,20 +45,20 @@ looptop:
|
|||
bswap ecx ;put loop count in CX
|
||||
dec cx ;count down loop
|
||||
jnz looptop
|
||||
</pre><!-- END CODE //-->
|
||||
</pre>
|
||||
|
||||
<h3><a id="Heading5"></a>Pushing and Popping Memory</h3>
|
||||
<h3 id="Heading5">Pushing and Popping Memory</h3>
|
||||
|
||||
<p>Pushing or popping a memory location, as in <b>PUSH WORD PTR [BX]</b> or <b>POP [MemVar]</b>, is a compact, easy way to get a value onto or off of the stack, especially when pushing parameters for calling a C-compatible function. However, on a 486, these are unattractive instructions from a performance perspective. Pushing a memory location takes four cycles; by contrast, loading a memory location into a register takes only one cycle, and pushing a register takes just 1 more cycle, for a total of two cycles. Therefore,</p><!-- CODE SNIP //-->
|
||||
<p>Pushing or popping a memory location, as in <b>PUSH WORD PTR [BX]</b> or <b>POP [MemVar]</b>, is a compact, easy way to get a value onto or off of the stack, especially when pushing parameters for calling a C-compatible function. However, on a 486, these are unattractive instructions from a performance perspective. Pushing a memory location takes four cycles; by contrast, loading a memory location into a register takes only one cycle, and pushing a register takes just 1 more cycle, for a total of two cycles. Therefore,</p>
|
||||
<pre>
|
||||
mov ax,[bx]
|
||||
push ax
|
||||
</pre><!-- END CODE SNIP //-->
|
||||
</pre>
|
||||
|
||||
<p>is twice as fast as</p><!-- CODE SNIP //-->
|
||||
<p>is twice as fast as</p>
|
||||
<pre>
|
||||
push word ptr [bx]
|
||||
</pre><!-- END CODE SNIP //-->
|
||||
</pre>
|
||||
|
||||
<p>and the only cost is that the previous contents of AX are destroyed.</p>
|
||||
|
||||
|
|
@ -81,16 +74,16 @@ push word ptr [bx]
|
|||
</tr>
|
||||
</table>
|
||||
|
||||
<h3><a id="Heading6"></a>Optimal 1-Bit Shifts and Rotates</h3>
|
||||
<h3 id="Heading6">Optimal 1-Bit Shifts and Rotates</h3>
|
||||
|
||||
<p>On a 486, the n-bit forms of the shift and rotate instructions—as in <b>ROR AX,2</b> and <b>SHL BX,9</b>—are 2-cycle instructions, but the 1-bit forms—as in <b>ROR AX,1</b> and <b>SHL BX,1—</b>are <i>3-cycle</i> instructions. Go figure.</p>
|
||||
|
||||
<p>Assemblers default to the 1-bit instruction for 1-bit shifts and rotates. That’s not unreasonable since the 1-bit form is a byte shorter and is just as fast as the n-bit forms on a 386 and faster on a 286, and the n-bit form doesn’t even exist on an 8088. In a really critical loop, however, it might be worth hand-assembling the n-bit form of a single-bit shift or rotate in order to save that cycle. The easiest way to do this is to assemble a 2-bit form of the desired instruction, as in <b>SHL AX,2,</b> then look at the hex codes that the assembler generates and use <b>DB</b> to insert them in your program code, with the value two replaced with the value one. For example, you could determine that <b>SHL AX,2</b> assembles to the bytes 0C1H 0E0H 002H, either by looking at the disassembly in a debugger or by having the assembler generate a listing file. You could then insert the n-bit version of <b>SHL AX,1</b> in your code as follows:</p><!-- CODE SNIP //-->
|
||||
<p>Assemblers default to the 1-bit instruction for 1-bit shifts and rotates. That’s not unreasonable since the 1-bit form is a byte shorter and is just as fast as the n-bit forms on a 386 and faster on a 286, and the n-bit form doesn’t even exist on an 8088. In a really critical loop, however, it might be worth hand-assembling the n-bit form of a single-bit shift or rotate in order to save that cycle. The easiest way to do this is to assemble a 2-bit form of the desired instruction, as in <b>SHL AX,2,</b> then look at the hex codes that the assembler generates and use <b>DB</b> to insert them in your program code, with the value two replaced with the value one. For example, you could determine that <b>SHL AX,2</b> assembles to the bytes 0C1H 0E0H 002H, either by looking at the disassembly in a debugger or by having the assembler generate a listing file. You could then insert the n-bit version of <b>SHL AX,1</b> in your code as follows:</p>
|
||||
<pre>
|
||||
mov ax,1
|
||||
db 0c1h, 0e0h, 001h
|
||||
mov dx,ax
|
||||
</pre><!-- END CODE SNIP //-->
|
||||
</pre>
|
||||
|
||||
<p>At the end of this sequence, DX will contain 2, and the fast n-bit version of <b>SHL AX,1</b> will have executed. If you use this approach, I’d recommend using a macro, rather than sticking DBs in the middle of your code.</p>
|
||||
|
||||
|
|
@ -113,10 +106,6 @@ mov dx,ax
|
|||
|
||||
<div align="center">
|
||||
Graphics Programming Black Book © 2001 Michael Abrash
|
||||
</div><!-- all of the reference materials (books) have the footer and subfoot reveresed -->
|
||||
<!-- reference_subfoot = footer -->
|
||||
<!-- reference_footer = subfoot -->
|
||||
<!-- BEGIN SUB FOOTER -->
|
||||
<!-- END FOOTER -->
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
29
13-04.html
29
13-04.html
|
|
@ -10,16 +10,9 @@
|
|||
<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: Aiming the 486</title><!-- HEADER -->
|
||||
<!-- Empty Reference Subhead -->
|
||||
<!--ISBN=1576101746//-->
|
||||
<!--TITLE=Michael Abrash's Graphics Programming Black Book Special Edition//-->
|
||||
<!--AUTHOR=Michael Abrash//-->
|
||||
<!--PUBLISHER=The Coriolis Group, Inc.//-->
|
||||
<title>Michael Abrash's Graphics Programming Black Book Special Edition: Aiming the 486</title>
|
||||
<meta name="chapter" content="13" />
|
||||
<meta name="pages" content="256-258" />
|
||||
<!--UNASSIGNED1//-->
|
||||
<!--UNASSIGNED2//-->
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
|
@ -37,12 +30,12 @@
|
|||
|
||||
<p><br /></p>
|
||||
|
||||
<h3><a id="Heading7"></a>32-Bit Addressing Modes</h3>
|
||||
<h3 id="Heading7">32-Bit Addressing Modes</h3>
|
||||
|
||||
<p>The 386 and 486 both support 32-bit addressing modes, in which any register may serve as the base memory addressing register, and almost any register may serve as the potentially scaled index register. For example,</p><!-- CODE SNIP //-->
|
||||
<p>The 386 and 486 both support 32-bit addressing modes, in which any register may serve as the base memory addressing register, and almost any register may serve as the potentially scaled index register. For example,</p>
|
||||
<pre>
|
||||
mov al,BaseTable[ecx+edx*4]
|
||||
</pre><!-- END CODE SNIP //-->
|
||||
</pre>
|
||||
|
||||
<p>uses a perfectly valid 32-bit address, with the byte accessed being the one at the offset in DS pointed to by the sum of EDX times 4 plus the offset of <b>BaseTable</b> plus ECX. This is a very powerful memory addressing scheme, far superior to 8088-style 16-bit addressing, but it’s not without its quirks and costs, so let’s take a quick look at 32-bit addressing. (By the way, 32-bit addressing is not limited to protected mode; 32-bit instructions may be used in real mode, although each instruction that uses 32-bit addressing must have an address-size prefix byte, and the presence of a prefix byte costs a cycle on a 486.)</p>
|
||||
|
||||
|
|
@ -60,16 +53,16 @@ mov al,BaseTable[ecx+edx*4]
|
|||
</tr>
|
||||
</table>
|
||||
|
||||
<p>However, because 32-bit addressing supports many more addressing combinations than 16-bit addressing, the Mod-R/M byte can’t describe all the combinations. Therefore, whenever an index register (as described above) is involved, a second byte, the SIB byte, follows the Mod-R/M byte to provide additional address information. Consequently, whenever you use a scaled memory addressing register or use the sum of two registers to point to memory, you automatically add 1 cycle and 1 byte to that instruction. This is not to say that you shouldn’t use index registers when they’re needed, but if you find yourself using them inside key loops, you should see if it’s possible to move the index calculation outside the loop as, for example, in a loop like this:</p><!-- CODE SNIP //-->
|
||||
<p>However, because 32-bit addressing supports many more addressing combinations than 16-bit addressing, the Mod-R/M byte can’t describe all the combinations. Therefore, whenever an index register (as described above) is involved, a second byte, the SIB byte, follows the Mod-R/M byte to provide additional address information. Consequently, whenever you use a scaled memory addressing register or use the sum of two registers to point to memory, you automatically add 1 cycle and 1 byte to that instruction. This is not to say that you shouldn’t use index registers when they’re needed, but if you find yourself using them inside key loops, you should see if it’s possible to move the index calculation outside the loop as, for example, in a loop like this:</p>
|
||||
<pre>
|
||||
LoopTop:
|
||||
add ax,DataTable[ebx*2]
|
||||
inc ebx
|
||||
dec cx
|
||||
jnz LoopTop
|
||||
</pre><!-- END CODE SNIP //-->
|
||||
</pre>
|
||||
|
||||
<p>You could change this to the following for greater performance:</p><!-- CODE SNIP //-->
|
||||
<p>You could change this to the following for greater performance:</p>
|
||||
<pre>
|
||||
add ebx,ebx ;ebx*2
|
||||
LoopTop:
|
||||
|
|
@ -78,7 +71,7 @@ LoopTop:
|
|||
dec cx
|
||||
jnz LoopTop
|
||||
shr ebx,1 ;ebx*2/2
|
||||
</pre><!-- END CODE SNIP //-->
|
||||
</pre>
|
||||
|
||||
<p>I’ll end this chapter with two more quirks of 32-bit addressing. First, as with 16-bit addressing, addressing that uses EBP as a base register both accesses the SS segment by default and always has a displacement of at least 1 byte. This reflects the common use of EBP to address a stack frame, but is worth keeping in mind if you should happen to use EBP to address non-stack memory.</p>
|
||||
|
||||
|
|
@ -101,10 +94,6 @@ LoopTop:
|
|||
|
||||
<div align="center">
|
||||
Graphics Programming Black Book © 2001 Michael Abrash
|
||||
</div><!-- all of the reference materials (books) have the footer and subfoot reveresed -->
|
||||
<!-- reference_subfoot = footer -->
|
||||
<!-- reference_footer = subfoot -->
|
||||
<!-- BEGIN SUB FOOTER -->
|
||||
<!-- END FOOTER -->
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
21
14-01.html
21
14-01.html
|
|
@ -10,16 +10,9 @@
|
|||
<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: Boyer-Moore String Searching</title><!-- HEADER -->
|
||||
<!-- Empty Reference Subhead -->
|
||||
<!--ISBN=1576101746//-->
|
||||
<!--TITLE=Michael Abrash's Graphics Programming Black Book Special Edition//-->
|
||||
<!--AUTHOR=Michael Abrash//-->
|
||||
<!--PUBLISHER=The Coriolis Group, Inc.//-->
|
||||
<title>Michael Abrash's Graphics Programming Black Book Special Edition: Boyer-Moore String Searching</title>
|
||||
<meta name="chapter" content="14" />
|
||||
<meta name="pages" content="260-263" />
|
||||
<!--UNASSIGNED1//-->
|
||||
<!--UNASSIGNED2//-->
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
|
@ -37,10 +30,10 @@
|
|||
|
||||
<p><br /></p>
|
||||
|
||||
<h2><a id="Heading1"></a>Chapter 14<br />
|
||||
<h2 id="Heading1">Chapter 14<br />
|
||||
Boyer-Moore String Searching</h2>
|
||||
|
||||
<h3><a id="Heading2"></a>Optimizing a Pretty Optimum Search Algorithm</h3>
|
||||
<h3 id="Heading2">Optimizing a Pretty Optimum Search Algorithm</h3>
|
||||
|
||||
<p>When you seem to be stumped, stop for a minute and <i>think.</i> All the information you need may be right in front of your nose if you just look at things a little differently. Here’s a case in point:</p>
|
||||
|
||||
|
|
@ -58,7 +51,7 @@
|
|||
|
||||
<p>As I said, sometimes everything you need to know is right in front of your nose. Which brings us to Boyer-Moore string searching.</p>
|
||||
|
||||
<h3><a id="Heading3"></a>String Searching Refresher</h3>
|
||||
<h3 id="Heading3">String Searching Refresher</h3>
|
||||
|
||||
<p>I’ve discussed string searching earlier in this book, in Chapters 5 and 9. You may want to refer back to these chapters for some background on string searching in general. I’m also going to use some of the code from that chapter as part of this chapter’s test suite. For further information, you may want to refer to the discussion of string searching in the excellent <i>Algorithms in C,</i> by Robert Sedgewick (Addison-Wesley), which served as the primary reference for this chapter. (If you look at Sedgewick, be aware that in the Boyer-Moore listing on page 288, there is a mistake: “j > 0” in the <b>for</b> loop should be “j >= 0,” unless I’m missing something.)</p>
|
||||
|
||||
|
|
@ -101,10 +94,6 @@
|
|||
|
||||
<div align="center">
|
||||
Graphics Programming Black Book © 2001 Michael Abrash
|
||||
</div><!-- all of the reference materials (books) have the footer and subfoot reveresed -->
|
||||
<!-- reference_subfoot = footer -->
|
||||
<!-- reference_footer = subfoot -->
|
||||
<!-- BEGIN SUB FOOTER -->
|
||||
<!-- END FOOTER -->
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
34
14-02.html
34
14-02.html
|
|
@ -10,16 +10,9 @@
|
|||
<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: Boyer-Moore String Searching</title><!-- HEADER -->
|
||||
<!-- Empty Reference Subhead -->
|
||||
<!--ISBN=1576101746//-->
|
||||
<!--TITLE=Michael Abrash's Graphics Programming Black Book Special Edition//-->
|
||||
<!--AUTHOR=Michael Abrash//-->
|
||||
<!--PUBLISHER=The Coriolis Group, Inc.//-->
|
||||
<title>Michael Abrash's Graphics Programming Black Book Special Edition: Boyer-Moore String Searching</title>
|
||||
<meta name="chapter" content="14" />
|
||||
<meta name="pages" content="263-267" />
|
||||
<!--UNASSIGNED1//-->
|
||||
<!--UNASSIGNED2//-->
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
|
@ -37,7 +30,7 @@
|
|||
|
||||
<p><br /></p>
|
||||
|
||||
<h3><a id="Heading4"></a>The Boyer-Moore Algorithm</h3>
|
||||
<h3 id="Heading4">The Boyer-Moore Algorithm</h3>
|
||||
|
||||
<p>All our <i>a priori</i> knowledge of string searching is stated above, but there’s another sort of knowledge—knowledge that’s generated dynamically. As we search through the buffer, we acquire information each time we check for a match. One sort of information that we acquire is based on partial matches; we can often skip ahead after partial matches because (take a deep breath!) by partially matching, we have already implicitly done a comparison of the partially matched buffer characters with all possible pattern start locations that overlap those partially-matched bytes.</p>
|
||||
|
||||
|
|
@ -59,25 +52,22 @@
|
|||
|
||||
<p>Figure 14.1 illustrates the operation of a Boyer-Moore search when the rightcharacter of the search pattern (which is the first character that’s compared at each location because we’re comparing backwards) mismatches with a buffer character that appears nowhere in the pattern. Figure 14.2 illustrates the operation of a partial match when the mismatch occurs with a character that’s not a pattern member. In this case, we can only skip ahead past the mismatch location, resulting in an advance of fewer bytes than the pattern length, and potentially as little as the same single byte distance by which the standard search approach advances.</p>
|
||||
|
||||
<p><a id="Fig1"><!-- </A><A HREF="javascript:displayWindow('images/14-01.jpg',413,306 )"> --><img src="images/14-01.jpg" /><br />
|
||||
<!-- </A>
|
||||
<BR><A HREF="javascript:displayWindow('images/14-01.jpg',413,306)"> --><b>Figure 14.1</b></a> <i>Mismatch on first character checked.</i></p>
|
||||
<p><a id="Fig1"><img src="images/14-01.jpg" /><br />
|
||||
<b>Figure 14.1</b></a> <i>Mismatch on first character checked.</i></p>
|
||||
|
||||
<p>What if the mismatch occurs with a buffer character that <i>does</i> occur in the pattern? Then we can’t skip past the mismatch location, but we can skip to whatever location aligns the rightmost occurrence of that character in the pattern with the mismatch location, as shown in Figure 14.3.</p>
|
||||
|
||||
<p>Basically, we exercise our right as members of a free society to compare strings in whichever direction we choose, and we choose to do so right to left, rather than the more intuitive left to right. Whenever we find a mismatch, we see what we can learn from the buffer character that failed to match the pattern. Imagine that we move the pattern to the right across the mismatch location until we find a start location that the mismatch does not eliminate as a possible match for the pattern. If the mismatch character doesn’t appear in the pattern, the pattern can move clear past the mismatch location. Otherwise, the pattern moves until a matching pattern byte lies atop the mismatch. That’s all there is to it!</p>
|
||||
|
||||
<p><a id="Fig2"><!-- </A><A HREF="javascript:displayWindow('images/14-02.jpg',406,306 )"> --><img src="images/14-02.jpg" /><br />
|
||||
<!-- </A>
|
||||
<BR><A HREF="javascript:displayWindow('images/14-02.jpg',406,306)"> --><b>Figure 14.2</b></a> <i>Mismatch on third character checked.</i></p>
|
||||
<p><a id="Fig2"><img src="images/14-02.jpg" /><br />
|
||||
<b>Figure 14.2</b></a> <i>Mismatch on third character checked.</i></p>
|
||||
|
||||
<h3><a id="Heading5"></a>Boyer-Moore: The Good and the Bad</h3>
|
||||
<h3 id="Heading5">Boyer-Moore: The Good and the Bad</h3>
|
||||
|
||||
<p>The worst case for this version of Boyer-Moore is that the pattern mismatches on the leftmost character—the last character compared—every time. Again, not very likely, but it is true that this version of Boyer-Moore performs better as there are fewer and shorter partial matches; ideally, the rightmost character would never match until the full match location was reached. Longer patterns, which make for longer skips, help Boyer-Moore, as does a long distance to the match location, which helps diffuse the overhead of building the table of distances to skip ahead on all the possible mismatch values.</p>
|
||||
|
||||
<p><a id="Fig3"><!-- </A><A HREF="javascript:displayWindow('images/14-03.jpg',407,304 )"> --><img src="images/14-03.jpg" /><br />
|
||||
<!-- </A>
|
||||
<BR><A HREF="javascript:displayWindow('images/14-03.jpg',407,304)"> --><b>Figure 14.3</b></a> <i>Mismatch on character that appears in pattern.</i></p>
|
||||
<p><a id="Fig3"><img src="images/14-03.jpg" /><br />
|
||||
<b>Figure 14.3</b></a> <i>Mismatch on character that appears in pattern.</i></p>
|
||||
|
||||
<p>The best case for Boyer-Moore is good indeed: About N/M comparisons are required, where N is the buffer length and M is the pattern length. This reflects the ability of Boyer-Moore to skip ahead by a full pattern length on a complete mismatch.</p>
|
||||
|
||||
|
|
@ -98,10 +88,6 @@
|
|||
|
||||
<div align="center">
|
||||
Graphics Programming Black Book © 2001 Michael Abrash
|
||||
</div><!-- all of the reference materials (books) have the footer and subfoot reveresed -->
|
||||
<!-- reference_subfoot = footer -->
|
||||
<!-- reference_footer = subfoot -->
|
||||
<!-- BEGIN SUB FOOTER -->
|
||||
<!-- END FOOTER -->
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
15
14-03.html
15
14-03.html
|
|
@ -10,16 +10,9 @@
|
|||
<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: Boyer-Moore String Searching</title><!-- HEADER -->
|
||||
<!-- Empty Reference Subhead -->
|
||||
<!--ISBN=1576101746//-->
|
||||
<!--TITLE=Michael Abrash's Graphics Programming Black Book Special Edition//-->
|
||||
<!--AUTHOR=Michael Abrash//-->
|
||||
<!--PUBLISHER=The Coriolis Group, Inc.//-->
|
||||
<title>Michael Abrash's Graphics Programming Black Book Special Edition: Boyer-Moore String Searching</title>
|
||||
<meta name="chapter" content="14" />
|
||||
<meta name="pages" content="267-268" />
|
||||
<!--UNASSIGNED1//-->
|
||||
<!--UNASSIGNED2//-->
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
|
@ -221,10 +214,6 @@
|
|||
|
||||
<div align="center">
|
||||
Graphics Programming Black Book © 2001 Michael Abrash
|
||||
</div><!-- all of the reference materials (books) have the footer and subfoot reveresed -->
|
||||
<!-- reference_subfoot = footer -->
|
||||
<!-- reference_footer = subfoot -->
|
||||
<!-- BEGIN SUB FOOTER -->
|
||||
<!-- END FOOTER -->
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
23
14-04.html
23
14-04.html
|
|
@ -10,16 +10,9 @@
|
|||
<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: Boyer-Moore String Searching</title><!-- HEADER -->
|
||||
<!-- Empty Reference Subhead -->
|
||||
<!--ISBN=1576101746//-->
|
||||
<!--TITLE=Michael Abrash's Graphics Programming Black Book Special Edition//-->
|
||||
<!--AUTHOR=Michael Abrash//-->
|
||||
<!--PUBLISHER=The Coriolis Group, Inc.//-->
|
||||
<title>Michael Abrash's Graphics Programming Black Book Special Edition: Boyer-Moore String Searching</title>
|
||||
<meta name="chapter" content="14" />
|
||||
<meta name="pages" content="268-271" />
|
||||
<!--UNASSIGNED1//-->
|
||||
<!--UNASSIGNED2//-->
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
|
@ -37,7 +30,7 @@
|
|||
|
||||
<p><br /></p>
|
||||
|
||||
<p><b>LISTING 14.1 L14-1.C</b></p><!-- CODE //-->
|
||||
<p><b>LISTING 14.1 L14-1.C</b></p>
|
||||
<pre>
|
||||
/* Searches a buffer for a specified pattern. In case of a mismatch,
|
||||
uses the value of the mismatched byte to skip across as many
|
||||
|
|
@ -124,9 +117,9 @@ unsigned char * FindString(unsigned char * BufferPtr,
|
|||
BufferPtr += Skip;
|
||||
}
|
||||
}
|
||||
</pre><!-- END CODE //-->
|
||||
</pre>
|
||||
|
||||
<p><b>LISTING 14.2 L14-2.C</b></p><!-- CODE //-->
|
||||
<p><b>LISTING 14.2 L14-2.C</b></p>
|
||||
<pre>
|
||||
/* Program to exercise buffer-search routines in Listings 14.1 & 14.3.
|
||||
(Must be modified to put copy of pattern as sentinel at end of the
|
||||
|
|
@ -182,7 +175,7 @@ void main() {
|
|||
}
|
||||
exit(0);
|
||||
}
|
||||
</pre><!-- END CODE //-->
|
||||
</pre>
|
||||
|
||||
<p>Well, architecture carries a lot of weight, but it sure as heck isn’t destiny. I had simply fallen into the trap of figuring that the algorithm was so clever that I didn’t have to do any thinking myself. The path leading to <b>REPNZ SCASB</b> from the original brute-force approach of <b>REPZ CMPSB</b> at every location had been based on my observation that the first character comparison at each buffer location usually fails. Why not apply the same concept to Boyer-Moore? Listing 14.3 is just like the standard implementation—except that it’s optimized to handle a first-comparison mismatch as quickly as possible in the loop at <b>QuickSearchLoop</b>, much as <b>REPNZ SCASB</b> optimizes first-comparison mismatches for the brute-force approach. The results in Table 14.1 speak for themselves; Listing 14.3 is more than twice as fast as what I assure you was already a nice, tight assembly implementation (and unrolling <b>QuickSearchLoop</b> could boost performance by up to 10 percent more). Listing 14.3 is also <i>four times</i> faster than <b>REPNZ SCASB</b> in one case.</p>
|
||||
|
||||
|
|
@ -203,10 +196,6 @@ void main() {
|
|||
|
||||
<div align="center">
|
||||
Graphics Programming Black Book © 2001 Michael Abrash
|
||||
</div><!-- all of the reference materials (books) have the footer and subfoot reveresed -->
|
||||
<!-- reference_subfoot = footer -->
|
||||
<!-- reference_footer = subfoot -->
|
||||
<!-- BEGIN SUB FOOTER -->
|
||||
<!-- END FOOTER -->
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
19
14-05.html
19
14-05.html
|
|
@ -10,16 +10,9 @@
|
|||
<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: Boyer-Moore String Searching</title><!-- HEADER -->
|
||||
<!-- Empty Reference Subhead -->
|
||||
<!--ISBN=1576101746//-->
|
||||
<!--TITLE=Michael Abrash's Graphics Programming Black Book Special Edition//-->
|
||||
<!--AUTHOR=Michael Abrash//-->
|
||||
<!--PUBLISHER=The Coriolis Group, Inc.//-->
|
||||
<title>Michael Abrash's Graphics Programming Black Book Special Edition: Boyer-Moore String Searching</title>
|
||||
<meta name="chapter" content="14" />
|
||||
<meta name="pages" content="271-274" />
|
||||
<!--UNASSIGNED1//-->
|
||||
<!--UNASSIGNED2//-->
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
|
@ -37,7 +30,7 @@
|
|||
|
||||
<p><br /></p>
|
||||
|
||||
<p><b>LISTING 14.3 L14-3.ASM</b></p><!-- CODE //-->
|
||||
<p><b>LISTING 14.3 L14-3.ASM</b></p>
|
||||
<pre>
|
||||
; Searches a buffer for a specified pattern. In case of a mismatch,
|
||||
; uses the value of the mismatched byte to skip across as many
|
||||
|
|
@ -199,7 +192,7 @@ Done:
|
|||
ret
|
||||
_FindString endp
|
||||
end
|
||||
</pre><!-- END CODE //-->
|
||||
</pre>
|
||||
|
||||
<p><br /></p>
|
||||
|
||||
|
|
@ -218,10 +211,6 @@ _FindString endp
|
|||
|
||||
<div align="center">
|
||||
Graphics Programming Black Book © 2001 Michael Abrash
|
||||
</div><!-- all of the reference materials (books) have the footer and subfoot reveresed -->
|
||||
<!-- reference_subfoot = footer -->
|
||||
<!-- reference_footer = subfoot -->
|
||||
<!-- BEGIN SUB FOOTER -->
|
||||
<!-- END FOOTER -->
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
23
14-06.html
23
14-06.html
|
|
@ -10,16 +10,9 @@
|
|||
<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: Boyer-Moore String Searching</title><!-- HEADER -->
|
||||
<!-- Empty Reference Subhead -->
|
||||
<!--ISBN=1576101746//-->
|
||||
<!--TITLE=Michael Abrash's Graphics Programming Black Book Special Edition//-->
|
||||
<!--AUTHOR=Michael Abrash//-->
|
||||
<!--PUBLISHER=The Coriolis Group, Inc.//-->
|
||||
<title>Michael Abrash's Graphics Programming Black Book Special Edition: Boyer-Moore String Searching</title>
|
||||
<meta name="chapter" content="14" />
|
||||
<meta name="pages" content="274-277" />
|
||||
<!--UNASSIGNED1//-->
|
||||
<!--UNASSIGNED2//-->
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
|
@ -41,11 +34,11 @@
|
|||
|
||||
<p>Know your data and use your smarts. Don’t stop thinking just because you’re implementing a big-name algorithm; you know more than it does.</p>
|
||||
|
||||
<h3><a id="Heading6"></a>Further Optimization of Boyer-Moore</h3>
|
||||
<h3 id="Heading6">Further Optimization of Boyer-Moore</h3>
|
||||
|
||||
<p>We can do substantially better yet than Listing 14.3 if we’re willing to accept tighter limits on the data. Limiting the length of the searched-for pattern to a maximum of 255 bytes allows us to use the <b>XLAT</b> instruction and generally tighten the critical loop. (Be aware, however, that <b>XLAT</b> is a relatively expensive instruction on the 486 and Pentium.) Putting a copy of the searched-for string at the end of the search buffer as a sentinel, so that the search never fails, frees us from counting down the buffer length, and makes it easy to unroll the critical loop. Listing 14.4, which implements these optimizations, is about 60 percent faster than Listing 14.3.</p>
|
||||
|
||||
<p><b>LISTING 14.4 L14-4.ASM</b></p><!-- CODE //-->
|
||||
<p><b>LISTING 14.4 L14-4.ASM</b></p>
|
||||
<pre>
|
||||
; Searches a buffer for a specified pattern. In case of a mismatch,
|
||||
; uses the value of the mismatched byte to skip across as many
|
||||
|
|
@ -193,11 +186,11 @@ Done:
|
|||
ret
|
||||
_FindString endp
|
||||
end
|
||||
</pre><!-- END CODE //-->
|
||||
</pre>
|
||||
|
||||
<p>Note that Table 14.1 includes the time required to build the skip table each time <b>FindString</b> is called. This time could be eliminated for all but the first search when repeatedly searching for a particular pattern, by building the skip table externally and passing a pointer to it as a parameter.</p>
|
||||
|
||||
<h3><a id="Heading7"></a>Know What You Know</h3>
|
||||
<h3 id="Heading7">Know What You Know</h3>
|
||||
|
||||
<p>Here we’ve turned up our nose at a repeated string instruction, we’ve gone against the grain by comparing backward, and yet we’ve speeded up our code quite a bit. All this without any restrictions or special requirements (excluding Listing 14.4)—and without any new information. Everything we needed was sitting there all along; we just needed to think to look at it.</p>
|
||||
|
||||
|
|
@ -220,10 +213,6 @@ _FindString endp
|
|||
|
||||
<div align="center">
|
||||
Graphics Programming Black Book © 2001 Michael Abrash
|
||||
</div><!-- all of the reference materials (books) have the footer and subfoot reveresed -->
|
||||
<!-- reference_subfoot = footer -->
|
||||
<!-- reference_footer = subfoot -->
|
||||
<!-- BEGIN SUB FOOTER -->
|
||||
<!-- END FOOTER -->
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
26
15-01.html
26
15-01.html
|
|
@ -10,16 +10,9 @@
|
|||
<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: Linked Lists and Unintended Challenges</title><!-- HEADER -->
|
||||
<!-- Empty Reference Subhead -->
|
||||
<!--ISBN=1576101746//-->
|
||||
<!--TITLE=Michael Abrash's Graphics Programming Black Book Special Edition//-->
|
||||
<!--AUTHOR=Michael Abrash//-->
|
||||
<!--PUBLISHER=The Coriolis Group, Inc.//-->
|
||||
<title>Michael Abrash's Graphics Programming Black Book Special Edition: Linked Lists and Unintended Challenges</title>
|
||||
<meta name="chapter" content="15" />
|
||||
<meta name="pages" content="279-284" />
|
||||
<!--UNASSIGNED1//-->
|
||||
<!--UNASSIGNED2//-->
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
|
@ -37,10 +30,10 @@
|
|||
|
||||
<p><br /></p>
|
||||
|
||||
<h2><a id="Heading1"></a>Chapter 15<br />
|
||||
<h2 id="Heading1">Chapter 15<br />
|
||||
Linked Lists and plain Unintended Challenges</h2>
|
||||
|
||||
<h3><a id="Heading2"></a>Unfamiliar Problems with Familiar Data Structures</h3>
|
||||
<h3 id="Heading2">Unfamiliar Problems with Familiar Data Structures</h3>
|
||||
|
||||
<p>After 21 years, this story still makes me wince. Oh, the humiliations I suffer for your enlightenment....</p>
|
||||
|
||||
|
|
@ -58,7 +51,7 @@
|
|||
|
||||
<p>Maybe you can—but I sure can’t. For example, consider the evolution of my understanding of linked lists.</p>
|
||||
|
||||
<h3><a id="Heading3"></a>Linked Lists</h3>
|
||||
<h3 id="Heading3">Linked Lists</h3>
|
||||
|
||||
<p>Linked lists are data structures composed of discrete elements, or nodes, joined together with links. In C, the links are typically pointers. Like all data structures, linked lists have their strengths and their weaknesses. Primary among the strengths are: simplicity; speedy sequential processing; ease and speed of insertion and deletion; the ability to mix nodes of various sizes and types; and the ability to handle variable amounts of data, especially when the total amount of data changes dynamically or is not always known beforehand. Weaknesses include: greater memory requirements than arrays (the pointers take up space); slow non-sequential processing, including finding arbitrary nodes; and an inability to backtrack, unless doubly-linked lists are used. Unfortunately, doubly linked lists need more memory, as well as processing time to maintain the backward links.</p>
|
||||
|
||||
|
|
@ -74,9 +67,8 @@
|
|||
|
||||
<p>The fundamental problem is that the model of Figure 15.1 unnecessarily complicates link manipulation. In order to delete a node, for example, you must change the preceding node’s <b>NextNode</b> pointer to point to the following node, as shown in Listing 15.1. (Listing 15.2 is the header file LLIST.H, which is <b>#include</b>d by all the linked list listings in this chapter.) Easy enough—unless the preceding node happens to be the head pointer, which doesn’t <i>have</i> a <b>NextNode</b> field, because it’s not a node, so Listing 15.1 won’t work. Cumbersome special code and extra information (a pointer to the head of the list) are required to handle the head-pointer case, as shown in Listing 15.3. (I’ll grant you that if you make the next-node pointer the first field in the <b>LinkNode</b> structure, at offset 0, then you could successfully point to the head pointer and pretend it was a <b>LinkNode</b> structure—but that’s an ugly and potentially dangerous trick, and we’ll see a better approach next.)</p>
|
||||
|
||||
<p><a id="Fig1"><!-- </A><A HREF="javascript:displayWindow('images/15-01.jpg',405,97 )"> --><img src="images/15-01.jpg" /><br />
|
||||
<!-- </A>
|
||||
<BR><A HREF="javascript:displayWindow('images/15-01.jpg',405,97)"> --><b>Figure 15.1</b></a> <i>The basic concept of a linked list.</i></p>
|
||||
<p><a id="Fig1"><img src="images/15-01.jpg" /><br />
|
||||
<b>Figure 15.1</b></a> <i>The basic concept of a linked list.</i></p>
|
||||
|
||||
<p><br /></p>
|
||||
|
||||
|
|
@ -95,10 +87,6 @@
|
|||
|
||||
<div align="center">
|
||||
Graphics Programming Black Book © 2001 Michael Abrash
|
||||
</div><!-- all of the reference materials (books) have the footer and subfoot reveresed -->
|
||||
<!-- reference_subfoot = footer -->
|
||||
<!-- reference_footer = subfoot -->
|
||||
<!-- BEGIN SUB FOOTER -->
|
||||
<!-- END FOOTER -->
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
43
15-02.html
43
15-02.html
|
|
@ -10,16 +10,9 @@
|
|||
<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: Linked Lists and Unintended Challenges</title><!-- HEADER -->
|
||||
<!-- Empty Reference Subhead -->
|
||||
<!--ISBN=1576101746//-->
|
||||
<!--TITLE=Michael Abrash's Graphics Programming Black Book Special Edition//-->
|
||||
<!--AUTHOR=Michael Abrash//-->
|
||||
<!--PUBLISHER=The Coriolis Group, Inc.//-->
|
||||
<title>Michael Abrash's Graphics Programming Black Book Special Edition: Linked Lists and Unintended Challenges</title>
|
||||
<meta name="chapter" content="15" />
|
||||
<meta name="pages" content="284-287" />
|
||||
<!--UNASSIGNED1//-->
|
||||
<!--UNASSIGNED2//-->
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
|
@ -37,7 +30,7 @@
|
|||
|
||||
<p><br /></p>
|
||||
|
||||
<p><b>LISTING 15.1 L15-1.C</b></p><!-- CODE //-->
|
||||
<p><b>LISTING 15.1 L15-1.C</b></p>
|
||||
<pre>
|
||||
/* Deletes the node in a linked list that follows the indicated node.
|
||||
Assumes list is headed by a dummy node, so no special testing for
|
||||
|
|
@ -51,9 +44,9 @@ struct LinkNode *DeleteNodeAfter(struct LinkNode *NodeToDeleteAfter)
|
|||
NodeToDeleteAfter->NextNode->NextNode;
|
||||
return(NodeToDeleteAfter);
|
||||
}
|
||||
</pre><!-- END CODE //-->
|
||||
</pre>
|
||||
|
||||
<p><b>LISTING 15.2 LLIST.H</b></p><!-- CODE //-->
|
||||
<p><b>LISTING 15.2 LLIST.H</b></p>
|
||||
<pre>
|
||||
/* Linked list header file. */
|
||||
#define MAX_TEXT_LENGTH 100 /* longest allowed Text field */
|
||||
|
|
@ -70,9 +63,9 @@ struct LinkNode *FindNodeBeforeValue(struct LinkNode *, int);
|
|||
struct LinkNode *InitLinkedList(void);
|
||||
struct LinkNode *InsertNodeSorted(struct LinkNode *,
|
||||
struct LinkNode *);
|
||||
</pre><!-- END CODE //-->
|
||||
</pre>
|
||||
|
||||
<p><b>LISTING 15.3 L15-3.C</b></p><!-- CODE //-->
|
||||
<p><b>LISTING 15.3 L15-3.C</b></p>
|
||||
<pre>
|
||||
/* Deletes the node in the specified linked list that follows the
|
||||
indicated node. List is headed by a head-of-list pointer; if the
|
||||
|
|
@ -92,7 +85,7 @@ struct LinkNode *DeleteNodeAfter(struct LinkNode **HeadOfListPtr,
|
|||
}
|
||||
return(NodeToDeleteAfter);
|
||||
}
|
||||
</pre><!-- END CODE //-->
|
||||
</pre>
|
||||
|
||||
<p>However, it is true that if you’re going to store a variety of types of structures in your linked lists, you should start each node with the <b>LinkNode</b> field. That way, the link pointer is in the same place in <i>every</i> structure, and the same linked list code can handle all of the structure types by casting them to the base link-node structure type. This is a less than elegant approach, but it works. C<small>++</small> can handle data mixing more cleanly than C, via derivation from a base link-node class.</p>
|
||||
|
||||
|
|
@ -100,13 +93,12 @@ struct LinkNode *DeleteNodeAfter(struct LinkNode **HeadOfListPtr,
|
|||
|
||||
<p>Similar problems with the head pointer crop up when you’re inserting nodes, and in fact in all link manipulation code. It’s easy to end up working with either pointers to pointers or lots of special-case code, and while those approaches work, they’re inelegant and inefficient.</p>
|
||||
|
||||
<h3><a id="Heading4"></a>Dummies and Sentinels</h3>
|
||||
<h3 id="Heading4">Dummies and Sentinels</h3>
|
||||
|
||||
<p>A far better approach is to use a <i>dummy node</i> for the head of the list, as shown in Figure 15.2. I invented this one for myself the next time I encountered linked lists, while designing a seed fill function for MetaWindows, back during my tenure at Metagraphics Corp. But I could have learned it by spending five minutes with Sedgewick’s book.</p>
|
||||
|
||||
<p><a id="Fig2"><!-- </A><A HREF="javascript:displayWindow('images/15-02.jpg',407,102 )"> --><img src="images/15-02.jpg" /><br />
|
||||
<!-- </A>
|
||||
<BR><A HREF="javascript:displayWindow('images/15-02.jpg',407,102)"> --><b>Figure 15.2</b></a> <i>Using a dummy head and tail node with a linked list.</i></p>
|
||||
<p><a id="Fig2"><img src="images/15-02.jpg" /><br />
|
||||
<b>Figure 15.2</b></a> <i>Using a dummy head and tail node with a linked list.</i></p>
|
||||
|
||||
<table width="100%">
|
||||
<tr>
|
||||
|
|
@ -120,7 +112,7 @@ struct LinkNode *DeleteNodeAfter(struct LinkNode **HeadOfListPtr,
|
|||
|
||||
<p>Figure 15.3 is a giant step in the right direction, but we can still make a few refinements. The inner loop of any code that scans through such a list has to perform a special test on each node to determine whether the tail has been reached. So, for example, code to find the first node containing a value field greater than or equal to a certain value has to perform two tests in the inner loop, as shown in Listing 15.4.</p>
|
||||
|
||||
<p><b>LISTING 15.4 L15-4.C</b></p><!-- CODE //-->
|
||||
<p><b>LISTING 15.4 L15-4.C</b></p>
|
||||
<pre>
|
||||
/* Finds the first node in a linked list with a value field greater
|
||||
than or equal to a key value, and returns a pointer to the node
|
||||
|
|
@ -144,13 +136,12 @@ struct LinkNode *FindNodeBeforeValueNotLess(
|
|||
return(NodePtr); /* success; return pointer to node preceding
|
||||
node that was >= */
|
||||
}
|
||||
</pre><!-- END CODE //-->
|
||||
</pre>
|
||||
|
||||
<p>Suppose, however, that we make the tail node a <i>sentinel</i> by giving it a value that is guaranteed to terminate the search, as shown in Figure 15.4. The list in Figure 15.4 has a sentinel with a value field of 32,767; since we’re working with integers, that’s the highest possible search value, and is guaranteed to satisfy any search that comes down the pike. The success or failure of the search can then be determined outside the loop, if necessary, by checking for the tail node’s special pointer—but the inside of the loop is streamlined to just one test, as shown in Listing 15.5. Not all linked lists lend themselves to sentinels, but the performance benefits are considerable for those lend themselves to sentinels, but the performance benefits are considerable for those that do.</p>
|
||||
|
||||
<p><a id="Fig3"><!-- </A><A HREF="javascript:displayWindow('images/15-03.jpg',407,104 )"> --><img src="images/15-03.jpg" /><br />
|
||||
<!-- </A>
|
||||
<BR><A HREF="javascript:displayWindow('images/15-03.jpg',407,104)"> --><b>Figure 15.3</b></a> <i>Representing an empty list.</i></p>
|
||||
<p><a id="Fig3"><img src="images/15-03.jpg" /><br />
|
||||
<b>Figure 15.3</b></a> <i>Representing an empty list.</i></p>
|
||||
|
||||
<p><br /></p>
|
||||
|
||||
|
|
@ -169,10 +160,6 @@ struct LinkNode *FindNodeBeforeValueNotLess(
|
|||
|
||||
<div align="center">
|
||||
Graphics Programming Black Book © 2001 Michael Abrash
|
||||
</div><!-- all of the reference materials (books) have the footer and subfoot reveresed -->
|
||||
<!-- reference_subfoot = footer -->
|
||||
<!-- reference_footer = subfoot -->
|
||||
<!-- BEGIN SUB FOOTER -->
|
||||
<!-- END FOOTER -->
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
35
15-03.html
35
15-03.html
|
|
@ -10,16 +10,9 @@
|
|||
<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: Linked Lists and Unintended Challenges</title><!-- HEADER -->
|
||||
<!-- Empty Reference Subhead -->
|
||||
<!--ISBN=1576101746//-->
|
||||
<!--TITLE=Michael Abrash's Graphics Programming Black Book Special Edition//-->
|
||||
<!--AUTHOR=Michael Abrash//-->
|
||||
<!--PUBLISHER=The Coriolis Group, Inc.//-->
|
||||
<title>Michael Abrash's Graphics Programming Black Book Special Edition: Linked Lists and Unintended Challenges</title>
|
||||
<meta name="chapter" content="15" />
|
||||
<meta name="pages" content="287-290" />
|
||||
<!--UNASSIGNED1//-->
|
||||
<!--UNASSIGNED2//-->
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
|
@ -37,7 +30,7 @@
|
|||
|
||||
<p><br /></p>
|
||||
|
||||
<p><b>LISTING 15.5 L15-5.C</b></p><!-- CODE //-->
|
||||
<p><b>LISTING 15.5 L15-5.C</b></p>
|
||||
<pre>
|
||||
/* Finds the first node in a value-sorted linked list that
|
||||
has a Value field greater than or equal to a key value, and
|
||||
|
|
@ -60,13 +53,12 @@ struct LinkNode *FindNodeBeforeValueNotLess(
|
|||
return(NodePtr); /* success; return pointer to node preceding
|
||||
node that was >= */
|
||||
}
|
||||
</pre><!-- END CODE //-->
|
||||
</pre>
|
||||
|
||||
<p><a id="Fig4"><!-- </A><A HREF="javascript:displayWindow('images/15-04.jpg',405,105 )"> --><img src="images/15-04.jpg" /><br />
|
||||
<!-- </A>
|
||||
<BR><A HREF="javascript:displayWindow('images/15-04.jpg',405,105)"> --><b>Figure 15.4</b></a> <i>List terminated by a sentinel.</i></p>
|
||||
<p><a id="Fig4"><img src="images/15-04.jpg" /><br />
|
||||
<b>Figure 15.4</b></a> <i>List terminated by a sentinel.</i></p>
|
||||
|
||||
<h3><a id="Heading5"></a>Circular Lists</h3>
|
||||
<h3 id="Heading5">Circular Lists</h3>
|
||||
|
||||
<p>One minor but elegant refinement yet remains: Use a single node as both the head <i>and</i> the tail of the list. We can do this by connecting the last node back to the first through the head/tail node in a circular fashion, as shown in Figure 15.5. This head/tail node can also, of course, be a sentinel; when it’s necessary to check for the end of the list explicitly, that can be done by comparing the current node pointer to the head pointer. If they’re equal, you’re at the head/tail node.</p>
|
||||
|
||||
|
|
@ -78,11 +70,10 @@ struct LinkNode *FindNodeBeforeValueNotLess(
|
|||
|
||||
<p>Contrast Figure 15.5 with Figure 15.1, and Listings 15.1, 15.5, 15.6, and 15.7 with Listings 15.3 and 15.4. Yes, linked lists are simple, but not so simple that a little knowledge doesn’t make a substantial difference. Make it a habit to read Knuth or Sedgewick or the like before you write a single line of code.</p>
|
||||
|
||||
<p><a id="Fig5"><!-- </A><A HREF="javascript:displayWindow('images/15-05.jpg',410,112 )"> --><img src="images/15-05.jpg" /><br />
|
||||
<!-- </A>
|
||||
<BR><A HREF="javascript:displayWindow('images/15-05.jpg',410,112)"> --><b>Figure 15.5</b></a> <i>Representing a circular list.</i></p>
|
||||
<p><a id="Fig5"><img src="images/15-05.jpg" /><br />
|
||||
<b>Figure 15.5</b></a> <i>Representing a circular list.</i></p>
|
||||
|
||||
<p><b>LISTING 15.6 L15-6.C</b></p><!-- CODE //-->
|
||||
<p><b>LISTING 15.6 L15-6.C</b></p>
|
||||
<pre>
|
||||
/* Suite of functions for maintaining a linked list sorted by
|
||||
ascending order of the Value field. The list is circular; that
|
||||
|
|
@ -149,7 +140,7 @@ struct LinkNode *InsertNodeSorted(struct LinkNode *HeadOfListNode,
|
|||
NodePtr->NextNode = NodeToInsert;
|
||||
return(NodePtr);
|
||||
}
|
||||
</pre><!-- END CODE //-->
|
||||
</pre>
|
||||
|
||||
<p><br /></p>
|
||||
|
||||
|
|
@ -168,10 +159,6 @@ struct LinkNode *InsertNodeSorted(struct LinkNode *HeadOfListNode,
|
|||
|
||||
<div align="center">
|
||||
Graphics Programming Black Book © 2001 Michael Abrash
|
||||
</div><!-- all of the reference materials (books) have the footer and subfoot reveresed -->
|
||||
<!-- reference_subfoot = footer -->
|
||||
<!-- reference_footer = subfoot -->
|
||||
<!-- BEGIN SUB FOOTER -->
|
||||
<!-- END FOOTER -->
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
29
15-04.html
29
15-04.html
|
|
@ -10,16 +10,9 @@
|
|||
<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: Linked Lists and Unintended Challenges</title><!-- HEADER -->
|
||||
<!-- Empty Reference Subhead -->
|
||||
<!--ISBN=1576101746//-->
|
||||
<!--TITLE=Michael Abrash's Graphics Programming Black Book Special Edition//-->
|
||||
<!--AUTHOR=Michael Abrash//-->
|
||||
<!--PUBLISHER=The Coriolis Group, Inc.//-->
|
||||
<title>Michael Abrash's Graphics Programming Black Book Special Edition: Linked Lists and Unintended Challenges</title>
|
||||
<meta name="chapter" content="15" />
|
||||
<meta name="pages" content="290-293" />
|
||||
<!--UNASSIGNED1//-->
|
||||
<!--UNASSIGNED2//-->
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
|
@ -37,7 +30,7 @@
|
|||
|
||||
<p><br /></p>
|
||||
|
||||
<p><b>LISTING 15.7 L15-7.ASM</b></p><!-- CODE //-->
|
||||
<p><b>LISTING 15.7 L15-7.ASM</b></p>
|
||||
<pre>
|
||||
; C near-callable assembly function for inserting a new node in a
|
||||
; linked list sorted by ascending order of the Value field. The list
|
||||
|
|
@ -98,9 +91,9 @@ SearchLoop:
|
|||
ret
|
||||
_InsertNodeSorted endp
|
||||
end
|
||||
</pre><!-- END CODE //-->
|
||||
</pre>
|
||||
|
||||
<p><b>LISTING 15.8 L15-8.C</b></p><!-- CODE //-->
|
||||
<p><b>LISTING 15.8 L15-8.C</b></p>
|
||||
<pre>
|
||||
/* Sample linked list program. Tested with Borland C++. */
|
||||
#include <stdlib.h>
|
||||
|
|
@ -183,9 +176,9 @@ void main()
|
|||
}
|
||||
}
|
||||
}
|
||||
</pre><!-- END CODE //-->
|
||||
</pre>
|
||||
|
||||
<h3><a id="Heading6"></a>Hi/Lo in 24 Bytes</h3>
|
||||
<h3 id="Heading6">Hi/Lo in 24 Bytes</h3>
|
||||
|
||||
<p>In one of my <i>PC TECHNIQUES</i> “Pushing the Envelope” columns, I passed along one of David Stafford’s fiendish programming puzzles: Write a C-callable function to find the greatest or smallest unsigned <b>int</b>. Not a big deal—except that David had <i>already</i> done it in 24 bytes, so the challenge was to do it in 24 bytes or less.</p>
|
||||
|
||||
|
|
@ -195,7 +188,7 @@ void main()
|
|||
|
||||
<p>Yes, a 24-byte hi/lo function is possible, anatomically improbable as it might seem. Which I guess goes to show that when one of David’s puzzles seems less than impossible, odds are you’re missing something. Listing 15.9 is David’s 24-byte solution, from which a lot may be learned if one reads closely enough.</p>
|
||||
|
||||
<p><b>LISTING 15.9 L15-9.ASM</b></p><!-- CODE //-->
|
||||
<p><b>LISTING 15.9 L15-9.ASM</b></p>
|
||||
<pre>
|
||||
; Find the greatest or smallest unsigned int.
|
||||
; C callable (small model); 24 bytes.
|
||||
|
|
@ -224,7 +217,7 @@ around: ja save
|
|||
jnz top
|
||||
|
||||
ret
|
||||
</pre><!-- END CODE //-->
|
||||
</pre>
|
||||
|
||||
<p>Before I end this chapter, let me say that I get a lot of feedback from my readers, and it’s much appreciated. Keep those cards, letters, and email messages coming. And if any of you know Jeannie Schweigert, have her drop me a line and let me know how she’s doing these days....</p>
|
||||
|
||||
|
|
@ -245,10 +238,6 @@ around: ja save
|
|||
|
||||
<div align="center">
|
||||
Graphics Programming Black Book © 2001 Michael Abrash
|
||||
</div><!-- all of the reference materials (books) have the footer and subfoot reveresed -->
|
||||
<!-- reference_subfoot = footer -->
|
||||
<!-- reference_footer = subfoot -->
|
||||
<!-- BEGIN SUB FOOTER -->
|
||||
<!-- END FOOTER -->
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
25
16-01.html
25
16-01.html
|
|
@ -10,16 +10,9 @@
|
|||
<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: There Ain't No Such Thing as the Fastest Code</title><!-- HEADER -->
|
||||
<!-- Empty Reference Subhead -->
|
||||
<!--ISBN=1576101746//-->
|
||||
<!--TITLE=Michael Abrash's Graphics Programming Black Book Special Edition//-->
|
||||
<!--AUTHOR=Michael Abrash//-->
|
||||
<!--PUBLISHER=The Coriolis Group, Inc.//-->
|
||||
<title>Michael Abrash's Graphics Programming Black Book Special Edition: There Ain't No Such Thing as the Fastest Code</title>
|
||||
<meta name="chapter" content="16" />
|
||||
<meta name="pages" content="295-300" />
|
||||
<!--UNASSIGNED1//-->
|
||||
<!--UNASSIGNED2//-->
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
|
@ -37,10 +30,10 @@
|
|||
|
||||
<p><br /></p>
|
||||
|
||||
<h2><a id="Heading1"></a>Chapter 16<br />
|
||||
<h2 id="Heading1">Chapter 16<br />
|
||||
There Ain’t No Such Thing as the Fastest Code</h2>
|
||||
|
||||
<h3><a id="Heading2"></a>Lessons Learned in the Pursuit of the Ultimate Word Counter</h3>
|
||||
<h3 id="Heading2">Lessons Learned in the Pursuit of the Ultimate Word Counter</h3>
|
||||
|
||||
<p>I remember reading an overview of C<small>++</small> development tools for Windows in a past issue of <i>PC Week</i>. In the lower left corner was the familiar box listing the 10 leading concerns of corporate buyers when it comes to C<small>++</small>. Boiled down, the list looked like this, in order of descending importance to buyers:</p>
|
||||
|
||||
|
|
@ -68,7 +61,7 @@
|
|||
|
||||
<p>Is something missing here? You bet your maximum <i>gluteus</i> something’s missing—nowhere on that list is there so much as one word about how fast the compiled code runs! I’m not saying that performance is everything, but optimization isn’t even down there at number 10, below online help! Ye gods and little fishes! We are talking here about people who would take a bus from LA to New York instead of a plane because it had a cleaner bathroom; who would choose a painting from a Holiday Inn over a Matisse because it had a fancier frame; who would buy a Yugo instead of—well, hell, anything—because it had a nice owner’s manual and particularly attractive keys. We are talking about people who are focusing on means, and have forgotten about ends. We are talking about people with no programming souls.</p>
|
||||
|
||||
<h3><a id="Heading3"></a>Counting Words in a Hurry</h3>
|
||||
<h3 id="Heading3">Counting Words in a Hurry</h3>
|
||||
|
||||
<p>What are we to make of this? At the very least, we can safely guess that very few corporate buyers ever enter optimization contests. Most of my readers do, however; in fact, far more than I thought ever would, but that gladdens me to no end. I issued my first optimization challenge in a “Pushing the Envelope” column in <i>PC TECHNIQUES</i> back in 1991, and was deluged by respondents who, one might also gather, do not live by <i>PC Week</i>.</p>
|
||||
|
||||
|
|
@ -128,7 +121,7 @@
|
|||
</tr>
|
||||
</table>
|
||||
|
||||
<p><b>LISTING 16.1 L16-1.C</b></p><!-- CODE //-->
|
||||
<p><b>LISTING 16.1 L16-1.C</b></p>
|
||||
<pre>
|
||||
/* Word-counting program. Tested with Borland C++ in C
|
||||
compilation mode and the small model. */
|
||||
|
|
@ -203,7 +196,7 @@
|
|||
return(0);
|
||||
}
|
||||
|
||||
</pre><!-- END CODE //-->
|
||||
</pre>
|
||||
|
||||
<p><br /></p>
|
||||
|
||||
|
|
@ -222,10 +215,6 @@
|
|||
|
||||
<div align="center">
|
||||
Graphics Programming Black Book © 2001 Michael Abrash
|
||||
</div><!-- all of the reference materials (books) have the footer and subfoot reveresed -->
|
||||
<!-- reference_subfoot = footer -->
|
||||
<!-- reference_footer = subfoot -->
|
||||
<!-- BEGIN SUB FOOTER -->
|
||||
<!-- END FOOTER -->
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
25
16-02.html
25
16-02.html
|
|
@ -10,16 +10,9 @@
|
|||
<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: There Ain't No Such Thing as the Fastest Code</title><!-- HEADER -->
|
||||
<!-- Empty Reference Subhead -->
|
||||
<!--ISBN=1576101746//-->
|
||||
<!--TITLE=Michael Abrash's Graphics Programming Black Book Special Edition//-->
|
||||
<!--AUTHOR=Michael Abrash//-->
|
||||
<!--PUBLISHER=The Coriolis Group, Inc.//-->
|
||||
<title>Michael Abrash's Graphics Programming Black Book Special Edition: There Ain't No Such Thing as the Fastest Code</title>
|
||||
<meta name="chapter" content="16" />
|
||||
<meta name="pages" content="300-303" />
|
||||
<!--UNASSIGNED1//-->
|
||||
<!--UNASSIGNED2//-->
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
|
@ -39,7 +32,7 @@
|
|||
|
||||
<p>Listing 16.2 is Listing 16.1 modified to call a function that scans each block for words, and Listing 16.3 contains an assembly function that counts words. Used together, Listings 16.2 and 16.3 are just about twice as fast as Listing 16.1, a good return for a little assembly language. Listing 16.3 is a pretty straightforward translation from C to assembly; the new code makes good use of registers, but the key code—determining whether each byte is a character or not—is still done with the same multiple-sequential-tests approach used by the code that the C compiler generates.</p>
|
||||
|
||||
<p><b>LISTING 16.2 L16-2.C</b></p><!-- CODE //-->
|
||||
<p><b>LISTING 16.2 L16-2.C</b></p>
|
||||
<pre>
|
||||
/* Word-counting program incorporating assembly language. Tested
|
||||
with Borland C<small>++</small> in C compilation mode & the small model. */
|
||||
|
|
@ -99,9 +92,9 @@
|
|||
printf(“\nTotal words in file: %lu\n”, WordCount);
|
||||
return(0);
|
||||
}
|
||||
</pre><!-- END CODE //-->
|
||||
</pre>
|
||||
|
||||
<p><b>LISTING 16.3 L16-3.ASM</b></p><!-- CODE //-->
|
||||
<p><b>LISTING 16.3 L16-3.ASM</b></p>
|
||||
<pre>
|
||||
; Assembly subroutine for Listing 16.2. Scans through Buffer, of
|
||||
; length BufferLength, counting words and updating WordCount as
|
||||
|
|
@ -183,9 +176,9 @@
|
|||
ret
|
||||
_ScanBuffer endp
|
||||
end
|
||||
</pre><!-- END CODE //-->
|
||||
</pre>
|
||||
|
||||
<h4 align="left"><a id="Heading4"></a>Which Way to Go from Here?</h4>
|
||||
<h4 align="left" id="Heading4">Which Way to Go from Here?</h4>
|
||||
|
||||
<p>We could rearrange the tests in light of the nature of the data being scanned; for example, we could perform the tests more efficiently by taking advantage of the knowledge that if a byte is less than ‘0,’ it’s either an apostrophe or not a character at all. However, that sort of fine-tuning is typically good for speedups of only 10 to 20 percent, and I’ve intentionally refrained from implementing this in Listing 16.3 to avoid pointing you down the wrong path; what we need is a different tack altogether. Ponder this. What we <i>really</i> want to know is nothing more than whether a byte is a character, not what sort of character it is. For each byte value, we want a yes/no status, and nothing else—and that description practically begs for a lookup table. Listing 16.4 uses a lookup table approach to boost performance another 50 percent, to three times the performance of the original C code. On a 20 MHz 386, this represents a change from 4.6 to 1.6 seconds, which could be significant—who likes to wait? On an 8088, the improvement in word-counting a large file could easily be 10 or 20 seconds, which is <i>definitely</i> significant.</p>
|
||||
|
||||
|
|
@ -206,10 +199,6 @@
|
|||
|
||||
<div align="center">
|
||||
Graphics Programming Black Book © 2001 Michael Abrash
|
||||
</div><!-- all of the reference materials (books) have the footer and subfoot reveresed -->
|
||||
<!-- reference_subfoot = footer -->
|
||||
<!-- reference_footer = subfoot -->
|
||||
<!-- BEGIN SUB FOOTER -->
|
||||
<!-- END FOOTER -->
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
21
16-03.html
21
16-03.html
|
|
@ -10,16 +10,9 @@
|
|||
<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: There Ain't No Such Thing as the Fastest Code</title><!-- HEADER -->
|
||||
<!-- Empty Reference Subhead -->
|
||||
<!--ISBN=1576101746//-->
|
||||
<!--TITLE=Michael Abrash's Graphics Programming Black Book Special Edition//-->
|
||||
<!--AUTHOR=Michael Abrash//-->
|
||||
<!--PUBLISHER=The Coriolis Group, Inc.//-->
|
||||
<title>Michael Abrash's Graphics Programming Black Book Special Edition: There Ain't No Such Thing as the Fastest Code</title>
|
||||
<meta name="chapter" content="16" />
|
||||
<meta name="pages" content="303-305" />
|
||||
<!--UNASSIGNED1//-->
|
||||
<!--UNASSIGNED2//-->
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
|
@ -37,7 +30,7 @@
|
|||
|
||||
<p><br /></p>
|
||||
|
||||
<p><b>LISTING 16.4 L16-4.ASM</b></p><!-- CODE //-->
|
||||
<p><b>LISTING 16.4 L16-4.ASM</b></p>
|
||||
<pre>
|
||||
; Assembly subroutine for Listing 16.2. Scans through Buffer, of
|
||||
; length BufferLength, counting words and updating WordCount as
|
||||
|
|
@ -130,7 +123,7 @@
|
|||
_ScanBuffer endp
|
||||
end
|
||||
|
||||
</pre><!-- END CODE //-->
|
||||
</pre>
|
||||
|
||||
<p>Listing 16.4 features several interesting tricks. First, it uses <b>LODSB</b> and <b>XLAT</b> in succession, a very neat way to get a pointed-to byte, advance the pointer, and look up the value indexed by the byte in a table, all with just two instruction bytes. (Interestingly, Listing 16.4 would probably run quite a bit better still on an 8088, where <b>LODSB</b> and <b>XLAT</b> have a greater advantage over conventional instructions. On the 486 and Pentium, however, <b>LODSB</b> and <b>XLAT</b> lose much of their appeal, and should be replaced with <b>MOV</b> instructions.) Better yet, <b>LODSB</b> and <b>XLAT</b> don’t alter the flags, so the Zero flag status set before <b>LODSB</b> is still around to be tested after <b>XLAT</b> .</p>
|
||||
|
||||
|
|
@ -146,7 +139,7 @@
|
|||
</tr>
|
||||
</table>
|
||||
|
||||
<h3><a id="Heading5"></a>Challenges and Hazards</h3>
|
||||
<h3 id="Heading5">Challenges and Hazards</h3>
|
||||
|
||||
<p>The challenge I put to the readers of <i>PC TECHNIQUES</i> was to write a faster module to replace Listing 16.4. The author of the code that counted the words in my secret test file fastest on my 20 MHz cached 386 would be the winner and receive Numerous Valuable Prizes.</p>
|
||||
|
||||
|
|
@ -171,10 +164,6 @@
|
|||
|
||||
<div align="center">
|
||||
Graphics Programming Black Book © 2001 Michael Abrash
|
||||
</div><!-- all of the reference materials (books) have the footer and subfoot reveresed -->
|
||||
<!-- reference_subfoot = footer -->
|
||||
<!-- reference_footer = subfoot -->
|
||||
<!-- BEGIN SUB FOOTER -->
|
||||
<!-- END FOOTER -->
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
25
16-04.html
25
16-04.html
|
|
@ -10,16 +10,9 @@
|
|||
<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: There Ain't No Such Thing as the Fastest Code</title><!-- HEADER -->
|
||||
<!-- Empty Reference Subhead -->
|
||||
<!--ISBN=1576101746//-->
|
||||
<!--TITLE=Michael Abrash's Graphics Programming Black Book Special Edition//-->
|
||||
<!--AUTHOR=Michael Abrash//-->
|
||||
<!--PUBLISHER=The Coriolis Group, Inc.//-->
|
||||
<title>Michael Abrash's Graphics Programming Black Book Special Edition: There Ain't No Such Thing as the Fastest Code</title>
|
||||
<meta name="chapter" content="16" />
|
||||
<meta name="pages" content="305-308" />
|
||||
<!--UNASSIGNED1//-->
|
||||
<!--UNASSIGNED2//-->
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
|
@ -39,18 +32,18 @@
|
|||
|
||||
<p>Truth to tell, I didn’t expect a three-times speedup; around two times was what I had in mind. Which just goes to show that any code can be made faster than you’d expect, if you think about it long enough and from many different perspectives. (The most potent word-counting technique seems to be a 64K lookup table that allows handling two bytes simultaneously. This is not the sort of technique one comes up with by brute-force optimization.) Thinking (or, worse yet, boasting) that your code is the fastest possible is rollescating on a tightrope in a hurricane; you’re due for a fall, if you catch my drift. Case in point: Terje Mathisen’s word-counting program.</p>
|
||||
|
||||
<h4 align="left"><a id="Heading6"></a>Blinding Yourself to a Better Approach</h4>
|
||||
<h4 align="left" id="Heading6">Blinding Yourself to a Better Approach</h4>
|
||||
|
||||
<p>Not so long ago, Terje Mathisen, who I introduced earlier in this book, wrote a very fast word-counting program, and posted it on Bix. When I say it was fast, I mean <i>fast;</i> this code was optimized like nobody’s business. We’re talking top-quality code here.</p>
|
||||
|
||||
<p>When the topic of optimizing came up in one of the Bix conferences, Terje’s program was mentioned, and he posted the following message: “I challenge BIXens (and especially <b>mabrash!</b>) to speed it up significantly. I would consider 5 percent a good result.” The clear implication was, “That code is as fast as it can possibly be.”</p>
|
||||
|
||||
<p>Naturally, it wasn’t; there ain’t no such thing as the fastest code (TANSTATFC? I agree, it doesn’t have the ring of TANSTAAFL). I pored over Terje’s 386 native-mode code, and found the critical inner loop, which was indeed as tight as one could imagine, consisting of just a few 386 native-mode instructions. However, one of the instructions was this:</p><!-- CODE SNIP //-->
|
||||
<p>Naturally, it wasn’t; there ain’t no such thing as the fastest code (TANSTATFC? I agree, it doesn’t have the ring of TANSTAAFL). I pored over Terje’s 386 native-mode code, and found the critical inner loop, which was indeed as tight as one could imagine, consisting of just a few 386 native-mode instructions. However, one of the instructions was this:</p>
|
||||
<pre>
|
||||
|
||||
CMP DH,[EBX+EAX]
|
||||
|
||||
</pre><!-- END CODE SNIP //-->
|
||||
</pre>
|
||||
|
||||
<p>Harmless enough, save for two things. First, EBX happened to be zero at this point (a leftover from an earlier version of the code, as it turned out), so it was superfluous as a memory-addressing component; this made it possible to use base-only addressing (<b>[EAX]</b>) rather than base+index addressing (<b>[EBX+EAX]</b>), which saves a cycle on the 386. Second: Changing the instruction to <b>CMP [EAX],DH</b> saved 2 cycles—just enough, by good fortune, to speed up the whole program by 5 percent.</p>
|
||||
|
||||
|
|
@ -64,7 +57,7 @@
|
|||
|
||||
<p>(Granted, <b>CMP [<i>mem</i>],<i>reg</i></b> is 1 cycle slower than <b>CMP <i>reg</i>,[<i>mem</i>]</b> on the 286, and they’re both the same on the 8088; in this case, though, the code was specific to the 386. In case you’re curious, both forms take 2 cycles on the 486; quite a lot faster, eh?)</p>
|
||||
|
||||
<h4 align="left"><a id="Heading7"></a>Watch Out for Luggable Assumptions!</h4>
|
||||
<h4 align="left" id="Heading7">Watch Out for Luggable Assumptions!</h4>
|
||||
|
||||
<p>The first lesson to be learned here is not to lug assumptions that may no longer be valid from the 8088/286 world into the wonderful new world of 386 native-mode programming. The second lesson is that after you’ve slaved over your code for a while, you’re in no shape to see its flaws, or to be able to get the new perspectives needed to speed it up. I’ll bet Terje looked at that <b>[EBX+EAX]</b> addressing a hundred times while trying to speed up his code, but he didn’t really see what it did; instead, he saw what it was supposed to do. Mental shortcuts like this are what enable us to deal with the complexities of assembly language without overloading after about 20 instructions, but they can be a major problem when looking over familiar code.</p>
|
||||
|
||||
|
|
@ -82,7 +75,7 @@
|
|||
|
||||
<p>By the way, Terje’s WC50 program is a full-fledged counting program; it counts characters, words, and lines, can handle multiple files, and lets you specify the characters that separate words, should you so desire. Source code is provided as part of the archive WC50 comes in. All in all, it’s a nice piece of work, and you might want to take a look at it if you’re interested in really fast assembly code. I wouldn’t call it the <i>fastest</i> word-counting code, though, because I would of course never be so foolish as to call <i>anything</i> the fastest.</p>
|
||||
|
||||
<h3><a id="Heading8"></a>The Astonishment of Right-Brain Optimization</h3>
|
||||
<h3 id="Heading8">The Astonishment of Right-Brain Optimization</h3>
|
||||
|
||||
<p>As it happened, the challenge I issued to my <i>PC TECHNIQUES</i> readers was a smashing success, with dozens of good entries. I certainly enjoyed it, even though I did have to look at a <i>lot</i> of tricky assembly code that I didn’t write—hard work under the best of circumstances. It was worth the trouble, though. The winning entry was an astonishing example of what assembly language can do in the right hands; on my 386, it was <i>four times</i> faster at word counting than the nice, tight assembly code I provided as a starting point—and about 13 times faster than the original C implementation. Attention, high-level language chauvinists: Is the speedup getting significant yet? Okay, maybe word counting isn’t the most critical application, but how would you like to have that kind of improvement in your compression software, or in your real-time games—or in Windows graphics?</p>
|
||||
|
||||
|
|
@ -105,10 +98,6 @@
|
|||
|
||||
<div align="center">
|
||||
Graphics Programming Black Book © 2001 Michael Abrash
|
||||
</div><!-- all of the reference materials (books) have the footer and subfoot reveresed -->
|
||||
<!-- reference_subfoot = footer -->
|
||||
<!-- reference_footer = subfoot -->
|
||||
<!-- BEGIN SUB FOOTER -->
|
||||
<!-- END FOOTER -->
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
19
16-05.html
19
16-05.html
|
|
@ -10,16 +10,9 @@
|
|||
<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: There Ain't No Such Thing as the Fastest Code</title><!-- HEADER -->
|
||||
<!-- Empty Reference Subhead -->
|
||||
<!--ISBN=1576101746//-->
|
||||
<!--TITLE=Michael Abrash's Graphics Programming Black Book Special Edition//-->
|
||||
<!--AUTHOR=Michael Abrash//-->
|
||||
<!--PUBLISHER=The Coriolis Group, Inc.//-->
|
||||
<title>Michael Abrash's Graphics Programming Black Book Special Edition: There Ain't No Such Thing as the Fastest Code</title>
|
||||
<meta name="chapter" content="16" />
|
||||
<meta name="pages" content="308-311" />
|
||||
<!--UNASSIGNED1//-->
|
||||
<!--UNASSIGNED2//-->
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
|
@ -165,7 +158,7 @@
|
|||
</tr>
|
||||
</table>
|
||||
|
||||
<p><b>LISTING 16.5 QSCAN3.ASM</b></p><!-- CODE //-->
|
||||
<p><b>LISTING 16.5 QSCAN3.ASM</b></p>
|
||||
<pre>
|
||||
; QSCAN3.ASM
|
||||
; David Stafford
|
||||
|
|
@ -345,7 +338,7 @@ jumping.
|
|||
.fardata WordTable
|
||||
include qscan3.inc ;built by MAKETAB
|
||||
end
|
||||
</pre><!-- END CODE //-->
|
||||
</pre>
|
||||
|
||||
<p><br /></p>
|
||||
|
||||
|
|
@ -364,10 +357,6 @@ jumping.
|
|||
|
||||
<div align="center">
|
||||
Graphics Programming Black Book © 2001 Michael Abrash
|
||||
</div><!-- all of the reference materials (books) have the footer and subfoot reveresed -->
|
||||
<!-- reference_subfoot = footer -->
|
||||
<!-- reference_footer = subfoot -->
|
||||
<!-- BEGIN SUB FOOTER -->
|
||||
<!-- END FOOTER -->
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
19
16-06.html
19
16-06.html
|
|
@ -10,16 +10,9 @@
|
|||
<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: There Ain't No Such Thing as the Fastest Code</title><!-- HEADER -->
|
||||
<!-- Empty Reference Subhead -->
|
||||
<!--ISBN=1576101746//-->
|
||||
<!--TITLE=Michael Abrash's Graphics Programming Black Book Special Edition//-->
|
||||
<!--AUTHOR=Michael Abrash//-->
|
||||
<!--PUBLISHER=The Coriolis Group, Inc.//-->
|
||||
<title>Michael Abrash's Graphics Programming Black Book Special Edition: There Ain't No Such Thing as the Fastest Code</title>
|
||||
<meta name="chapter" content="16" />
|
||||
<meta name="pages" content="311-313" />
|
||||
<!--UNASSIGNED1//-->
|
||||
<!--UNASSIGNED2//-->
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
|
@ -37,7 +30,7 @@
|
|||
|
||||
<p><br /></p>
|
||||
|
||||
<h3><a id="Heading9"></a>Levels of Optimization</h3>
|
||||
<h3 id="Heading9">Levels of Optimization</h3>
|
||||
|
||||
<p>Three levels of optimization were evident in the word-counting entries I received in response to my challenge. I’d briefly describe them as “fine-tuning,” “new perspective,” and “table-driven state machine.” The latter categories produce faster code, but, by the same token, they are harder to design, harder to implement, and more difficult to understand, so they’re suitable for only the most demanding applications. (Heck, I don’t even guarantee that David Stafford’s entry works perfectly, although, knowing him, it probably does; the more complex and cryptic the code, the greater the chance for obscure bugs.)</p>
|
||||
|
||||
|
|
@ -49,7 +42,7 @@
|
|||
</tr>
|
||||
</table>
|
||||
|
||||
<h4 align="left"><a id="Heading10"></a>Optimization Level 1: Good Code</h4>
|
||||
<h4 align="left" id="Heading10">Optimization Level 1: Good Code</h4>
|
||||
|
||||
<p>The first level of optimization involves fine-tuning and clever use of the instruction set. The basic framework is still the same as my code (which in turn is basically the same as that of the original C code), but that framework is implemented more efficiently.</p>
|
||||
|
||||
|
|
@ -80,10 +73,6 @@
|
|||
|
||||
<div align="center">
|
||||
Graphics Programming Black Book © 2001 Michael Abrash
|
||||
</div><!-- all of the reference materials (books) have the footer and subfoot reveresed -->
|
||||
<!-- reference_subfoot = footer -->
|
||||
<!-- reference_footer = subfoot -->
|
||||
<!-- BEGIN SUB FOOTER -->
|
||||
<!-- END FOOTER -->
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
21
16-07.html
21
16-07.html
|
|
@ -10,16 +10,9 @@
|
|||
<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: There Ain't No Such Thing as the Fastest Code</title><!-- HEADER -->
|
||||
<!-- Empty Reference Subhead -->
|
||||
<!--ISBN=1576101746//-->
|
||||
<!--TITLE=Michael Abrash's Graphics Programming Black Book Special Edition//-->
|
||||
<!--AUTHOR=Michael Abrash//-->
|
||||
<!--PUBLISHER=The Coriolis Group, Inc.//-->
|
||||
<title>Michael Abrash's Graphics Programming Black Book Special Edition: There Ain't No Such Thing as the Fastest Code</title>
|
||||
<meta name="chapter" content="16" />
|
||||
<meta name="pages" content="313-316" />
|
||||
<!--UNASSIGNED1//-->
|
||||
<!--UNASSIGNED2//-->
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
|
@ -37,7 +30,7 @@
|
|||
|
||||
<p><br /></p>
|
||||
|
||||
<p><b>Listing 16.6 OPT2.ASM</b></p><!-- CODE //-->
|
||||
<p><b>Listing 16.6 OPT2.ASM</b></p>
|
||||
<pre>
|
||||
;
|
||||
; Opt2 Final optimization word count
|
||||
|
|
@ -143,9 +136,9 @@
|
|||
ret
|
||||
_ScanBuffer endp
|
||||
end
|
||||
</pre><!-- END CODE //-->
|
||||
</pre>
|
||||
|
||||
<h3><a id="Heading11"></a>Level 2: A New Perspective</h3>
|
||||
<h3 id="Heading11">Level 2: A New Perspective</h3>
|
||||
|
||||
<p>The second level of optimization is one of breaking out of the mode of thinking established by my original code. Some entrants clearly did exactly that. They stepped back, thought about what the code actually needed to do, rather than just improving how it already worked, and implemented code that sprang from that new perspective.</p>
|
||||
|
||||
|
|
@ -176,10 +169,6 @@
|
|||
|
||||
<div align="center">
|
||||
Graphics Programming Black Book © 2001 Michael Abrash
|
||||
</div><!-- all of the reference materials (books) have the footer and subfoot reveresed -->
|
||||
<!-- reference_subfoot = footer -->
|
||||
<!-- reference_footer = subfoot -->
|
||||
<!-- BEGIN SUB FOOTER -->
|
||||
<!-- END FOOTER -->
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
37
16-08.html
37
16-08.html
|
|
@ -10,16 +10,9 @@
|
|||
<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: There Ain't No Such Thing as the Fastest Code</title><!-- HEADER -->
|
||||
<!-- Empty Reference Subhead -->
|
||||
<!--ISBN=1576101746//-->
|
||||
<!--TITLE=Michael Abrash's Graphics Programming Black Book Special Edition//-->
|
||||
<!--AUTHOR=Michael Abrash//-->
|
||||
<!--PUBLISHER=The Coriolis Group, Inc.//-->
|
||||
<title>Michael Abrash's Graphics Programming Black Book Special Edition: There Ain't No Such Thing as the Fastest Code</title>
|
||||
<meta name="chapter" content="16" />
|
||||
<meta name="pages" content="316-319" />
|
||||
<!--UNASSIGNED1//-->
|
||||
<!--UNASSIGNED2//-->
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
|
@ -37,7 +30,7 @@
|
|||
|
||||
<p><br /></p>
|
||||
|
||||
<p><b>Listing 16.7 L16-7.ASM</b></p><!-- CODE //-->
|
||||
<p><b>Listing 16.7 L16-7.ASM</b></p>
|
||||
<pre>
|
||||
ScanLoop:
|
||||
lodsw ;get the next 2 bytes (AL = first, AH = 2nd)
|
||||
|
|
@ -53,13 +46,13 @@
|
|||
dec dx
|
||||
jnz ScanLoop
|
||||
|
||||
</pre><!-- END CODE //-->
|
||||
</pre>
|
||||
|
||||
<p>John later divides the transition count by two to get the word count. (Food for thought: It’s also possible to use <b>CMP</b> and <b>ADC</b> to detect words without branching.)</p>
|
||||
|
||||
<p>John’s approach makes it clear that word-counting is nothing more than a fairly simple state machine. The interesting part, of course, is building the fastest state machine.</p>
|
||||
|
||||
<h4 align="left"><a id="Heading12"></a>Level 3: Breakthrough</h4>
|
||||
<h4 align="left" id="Heading12">Level 3: Breakthrough</h4>
|
||||
|
||||
<p>The boundaries between the levels of optimization are not sharply defined. In a sense, level 3 optimization is just like levels 1 and 2, but more so. At level 3, one takes whatever level 2 perspective seems most promising, and implements it as efficiently as possible on the x86. Even more than at level 2, at level 3 this means breaking out of familiar patterns of thinking.</p>
|
||||
|
||||
|
|
@ -67,7 +60,7 @@
|
|||
|
||||
<p>The key concept at level 3 is the use of a massive (64K) lookup table that processes byte sequences directly into word-count actions. With such a table, it’s possible to look up the appropriate action for two bytes simultaneously in just a few instructions; next, I’m going to look at the inspired and highly unusual way that David’s code, shown in Listing 16.5, does exactly that. (Before assembling Listing 16.5, you must run the C code in Listing 16.8, to generate an include file defining the 64K lookup table. When you assemble Listing 16.5, TASM will report a “location counter overflow” warning; ignore it.)</p>
|
||||
|
||||
<p><b>LISTING 16.8 MAKETAB.C</b></p><!-- CODE //-->
|
||||
<p><b>LISTING 16.8 MAKETAB.C</b></p>
|
||||
<pre>
|
||||
// MAKETAB.C — Build QSCAN3.INC for QSCAN3.ASM
|
||||
|
||||
|
|
@ -102,27 +95,25 @@
|
|||
fclose( t );
|
||||
}
|
||||
|
||||
</pre><!-- END CODE //-->
|
||||
</pre>
|
||||
|
||||
<p>David’s approach is simplicity itself, although his implementation arguably is not. Consider any three sequential bytes in the buffer. Those three bytes define two potential places where a word might be counted, as shown in Figure 16.1. Given the separator/non-separator states of the three bytes, you can instantly determine whether to count a word or not; you count a word if and only if somewhere in the sequence there is a non-separator followed by a separator. Note that a maximum of one word can be counted per three-byte sequence.</p>
|
||||
|
||||
<p>The trick, then, is to identify the separator/not statuses of each set of three bytes and turn them into a 1 (count word) or 0 (don’t count word), as quickly as possible. Assuming that the separator/not status for the first byte is in the Carry flag, this is easily accomplished by a lookup in a 64K table, based on the Carry flag and the other two bytes, as shown in Figure 16.2. (Remember that we’re counting 7-bit ASCII here, so the high bit is ignored.) Thus, David is able to add the word/not status for each pair of bytes to the main word count simply by getting the two bytes, working in the carry status from the last byte, and using the resulting value to index into the 64K table, adding in the 1 or 0 value found in that table. A sequence of <b>MOV/ADC/ADD</b> suffices to perform all word-counting tasks for a pair of bytes. Three instructions, no branches—pretty nearly perfect code.</p>
|
||||
|
||||
<p><a id="Fig1"><!-- </A><A HREF="javascript:displayWindow('images/16-01.jpg',410,122 )"> --><img src="images/16-01.jpg" /><br />
|
||||
<!-- </A>
|
||||
<BR><A HREF="javascript:displayWindow('images/16-01.jpg',410,122)"> --><b>Figure 16.1</b></a> <i>The two potential word count locations.</i></p>
|
||||
<p><a id="Fig1"><img src="images/16-01.jpg" /><br />
|
||||
<b>Figure 16.1</b></a> <i>The two potential word count locations.</i></p>
|
||||
|
||||
<p>One detail remains to be attended to: setting the Carry flag for next time if the last byte was a non-separator. David does this in a bizarre and incredibly effective way: He presets the high bit of the count, and sets the high bit in the lookup table for those entries looked up by non-separators. When a non-separator’s lookup entry is added to the count, it will produce a carry, as desired. The high bit of the count is masked off before being added to the total count, so David is essentially using different parts of the count variables for different purposes (counting, and setting the Carry flag).</p>
|
||||
|
||||
<p><a id="Fig2"><!-- </A><A HREF="javascript:displayWindow('images/16-02.jpg',408,232 )"> --><img src="images/16-02.jpg" /><br />
|
||||
<!-- </A>
|
||||
<BR><A HREF="javascript:displayWindow('images/16-02.jpg',408,232)"> --><b>Figure 16.2</b></a> <i>Looking up a word count status.</i></p>
|
||||
<p><a id="Fig2"><img src="images/16-02.jpg" /><br />
|
||||
<b>Figure 16.2</b></a> <i>Looking up a word count status.</i></p>
|
||||
|
||||
<p>There are a number of other interesting details in David’s code, including the unrolling of the loop 64 times, so that 256 bytes in a row are processed without a single branch. Unfortunately, I lack the space to discuss Listing 16.5 any further. Perhaps that’s not so unfortunate, after all; I’d hate to deny you the pleasure of discovering the wonders of this rather remarkable code yourself. I will say one more thing, though. The cycle count for David’s inner loop is 6.5 cycles per byte processed, and the actual measured time for his routine, overhead and all, is 7.9 cycles/byte. The original C code clocked in at around 100 cycles/byte.</p>
|
||||
|
||||
<p>Enough said, I trust.</p>
|
||||
|
||||
<h4 align="left"><a id="Heading13"></a>Enough Word Counting Already!</h4>
|
||||
<h4 align="left" id="Heading13">Enough Word Counting Already!</h4>
|
||||
|
||||
<p>Before I finish up this chapter, I’d like to mention that Terje Mathisen’s WC word-counting program, which I’ve mentioned previously and which is available, with source, on Bix, is in the ballpark with David’s code for performance. What’s more, Terje’s program handles 8-bit ASCII, counts lines as well as words, and supports user-definable separator sets. It’s wonderful code, well worth a look; it also happens to be a great word-counting utility. By the way, Terje builds his 64K table on the fly, at program initialization; this allows for customized tables, shrinks the size of the EXE, and, according to Terje’s calculations, takes less time than loading the table off disk as part of the EXE.</p>
|
||||
|
||||
|
|
@ -145,10 +136,6 @@
|
|||
|
||||
<div align="center">
|
||||
Graphics Programming Black Book © 2001 Michael Abrash
|
||||
</div><!-- all of the reference materials (books) have the footer and subfoot reveresed -->
|
||||
<!-- reference_subfoot = footer -->
|
||||
<!-- reference_footer = subfoot -->
|
||||
<!-- BEGIN SUB FOOTER -->
|
||||
<!-- END FOOTER -->
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
23
17-01.html
23
17-01.html
|
|
@ -10,16 +10,9 @@
|
|||
<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 Game of Life</title><!-- HEADER -->
|
||||
<!-- Empty Reference Subhead -->
|
||||
<!--ISBN=1576101746//-->
|
||||
<!--TITLE=Michael Abrash's Graphics Programming Black Book Special Edition//-->
|
||||
<!--AUTHOR=Michael Abrash//-->
|
||||
<!--PUBLISHER=The Coriolis Group, Inc.//-->
|
||||
<title>Michael Abrash's Graphics Programming Black Book Special Edition: The Game of Life</title>
|
||||
<meta name="chapter" content="17" />
|
||||
<meta name="pages" content="322-325" />
|
||||
<!--UNASSIGNED1//-->
|
||||
<!--UNASSIGNED2//-->
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
|
@ -37,16 +30,16 @@
|
|||
|
||||
<p><br /></p>
|
||||
|
||||
<h2><a id="Heading1"></a>Chapter 17<br />
|
||||
<h2 id="Heading1">Chapter 17<br />
|
||||
The Game of Life</h2>
|
||||
|
||||
<h3><a id="Heading2"></a>The Triumph of Algorithmic Optimization in a Cellular Automata Game</h3>
|
||||
<h3 id="Heading2">The Triumph of Algorithmic Optimization in a Cellular Automata Game</h3>
|
||||
|
||||
<p>I’ve spent a lot of my life discussing assembly language optimization, which I consider to be an important and underappreciated topic. However, I’d like to take this opportunity to point out that there is much, much more to optimization than assembly language. Assembly is essential for absolute maximum performance, but it’s not the only ingredient; necessary but not sufficient, if you catch my drift—and not even necessary, if you’re looking for improved but not maximum performance. You’ve heard it a thousand times: Optimize your algorithm first. Devise new approaches. Or, as Knuth said, <i>Premature optimization is the root of all evil.</i></p>
|
||||
|
||||
<p>This is, of course, old hat, stuff you know like the back of your hand. Or is it? As Jeff Duntemann pointed out to me the other day, performance programmers are made, not born. While I’m merrily gallivanting around in this book optimizing 486 pipelining and turning simple tasks into horribly complicated and terrifyingly fast state machines, many of you are still developing your basic optimization skills. I don’t want to shortchange those of you in the latter category, so in this chapter, we’ll discuss some high-level language optimizations that can be applied by mere mortals within a reasonable period of time. We’re going to examine a complete optimization process, from start to finish, and what we will find is that it’s possible to get a 50-times speed-up without using <i>one byte of assembly!</i> It’s all a matter of perspective—how you look at your code and data.</p>
|
||||
|
||||
<h3><a id="Heading3"></a>Conway’s Game</h3>
|
||||
<h3 id="Heading3">Conway’s Game</h3>
|
||||
|
||||
<p>The program that we’re going to optimize is Conway’s famous Game of Life, long-ago favorite of the hackers at MIT’s AI Lab. If you’ve never seen it, let me assure you: Life is <i>neat,</i> and more than a little hypnotic. Fractals have been the hot graphics topic in recent years, but for eye-catching dazzle, Life is hard to beat.</p>
|
||||
|
||||
|
|
@ -54,7 +47,7 @@
|
|||
|
||||
<p>First, I’ll describe the ground rules of Life, implement a very straightforward version in C<small>++</small>, and then speed that version up by about eight times without using any drastically different approaches or any assembly. This may be a little tame for some of you, but be patient; for after that, we’ll haul out the big guns and move into the 30 to 40 times speed-up range. Then in the next chapter, I’ll show you how several programmers <i>really</i> floored it in taking me up on my second Optimization Challenge, which involved the Game of Life.</p>
|
||||
|
||||
<h4 align="left"><a id="Heading4"></a>The Rules of the Game</h4>
|
||||
<h4 align="left" id="Heading4">The Rules of the Game</h4>
|
||||
|
||||
<p>The Game of Life is ridiculously simple. There is a cellmap, consisting of a rectangular matrix of cells, each of which may initially be either on or off. Each cell has eight neighbors: two horizontally, two vertically, and four diagonally. For each succeeding generation of cells, the game logic determines whether each cell will be on or off according to the following rules:</p>
|
||||
|
||||
|
|
@ -85,10 +78,6 @@
|
|||
|
||||
<div align="center">
|
||||
Graphics Programming Black Book © 2001 Michael Abrash
|
||||
</div><!-- all of the reference materials (books) have the footer and subfoot reveresed -->
|
||||
<!-- reference_subfoot = footer -->
|
||||
<!-- reference_footer = subfoot -->
|
||||
<!-- BEGIN SUB FOOTER -->
|
||||
<!-- END FOOTER -->
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
23
17-02.html
23
17-02.html
|
|
@ -10,16 +10,9 @@
|
|||
<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 Game of Life</title><!-- HEADER -->
|
||||
<!-- Empty Reference Subhead -->
|
||||
<!--ISBN=1576101746//-->
|
||||
<!--TITLE=Michael Abrash's Graphics Programming Black Book Special Edition//-->
|
||||
<!--AUTHOR=Michael Abrash//-->
|
||||
<!--PUBLISHER=The Coriolis Group, Inc.//-->
|
||||
<title>Michael Abrash's Graphics Programming Black Book Special Edition: The Game of Life</title>
|
||||
<meta name="chapter" content="17" />
|
||||
<meta name="pages" content="325-329" />
|
||||
<!--UNASSIGNED1//-->
|
||||
<!--UNASSIGNED2//-->
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
|
@ -37,7 +30,7 @@
|
|||
|
||||
<p><br /></p>
|
||||
|
||||
<p><b>LISTING 17.1 L17-1.CPP</b></p><!-- CODE //-->
|
||||
<p><b>LISTING 17.1 L17-1.CPP</b></p>
|
||||
<pre>
|
||||
/* C++ Game of Life implementation for any mode for which mode set
|
||||
and draw pixel functions can be provided.
|
||||
|
|
@ -233,9 +226,9 @@ void cellmap::next_generation(cellmap& next_map)
|
|||
}
|
||||
}
|
||||
}
|
||||
</pre><!-- END CODE //-->
|
||||
</pre>
|
||||
|
||||
<p><b>LISTING 17.2 L17-2.CPP</b></p><!-- CODE //-->
|
||||
<p><b>LISTING 17.2 L17-2.CPP</b></p>
|
||||
<pre>
|
||||
/* VGA mode 13h functions for Game of Life.
|
||||
Tested with Borland C++. */
|
||||
|
|
@ -293,7 +286,7 @@ void show_text(int x, int y, char *text)
|
|||
gotoxy(TEXT_X_OFFSET + x, y);
|
||||
puts(text);
|
||||
}
|
||||
</pre><!-- END CODE //-->
|
||||
</pre>
|
||||
|
||||
<p><br /></p>
|
||||
|
||||
|
|
@ -312,10 +305,6 @@ void show_text(int x, int y, char *text)
|
|||
|
||||
<div align="center">
|
||||
Graphics Programming Black Book © 2001 Michael Abrash
|
||||
</div><!-- all of the reference materials (books) have the footer and subfoot reveresed -->
|
||||
<!-- reference_subfoot = footer -->
|
||||
<!-- reference_footer = subfoot -->
|
||||
<!-- BEGIN SUB FOOTER -->
|
||||
<!-- END FOOTER -->
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
19
17-03.html
19
17-03.html
|
|
@ -10,16 +10,9 @@
|
|||
<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 Game of Life</title><!-- HEADER -->
|
||||
<!-- Empty Reference Subhead -->
|
||||
<!--ISBN=1576101746//-->
|
||||
<!--TITLE=Michael Abrash's Graphics Programming Black Book Special Edition//-->
|
||||
<!--AUTHOR=Michael Abrash//-->
|
||||
<!--PUBLISHER=The Coriolis Group, Inc.//-->
|
||||
<title>Michael Abrash's Graphics Programming Black Book Special Edition: The Game of Life</title>
|
||||
<meta name="chapter" content="17" />
|
||||
<meta name="pages" content="329-331" />
|
||||
<!--UNASSIGNED1//-->
|
||||
<!--UNASSIGNED2//-->
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
|
@ -37,7 +30,7 @@
|
|||
|
||||
<p><br /></p>
|
||||
|
||||
<h3><a id="Heading5"></a>Where Does the Time Go?</h3>
|
||||
<h3 id="Heading5">Where Does the Time Go?</h3>
|
||||
|
||||
<p>How slow is Listing 17.1? Table 17.1 shows that even on a 486, Listing 17.1 does fewer than three 96x96 generations per second. (The times in Table 17.1 are for 1,000 generations of a 96x96 cell map with <b>seed=1, LIMIT_18_HZ=0, WRAP_EDGES=1,</b> and <b>magnifier=2,</b> running on a 33 MHz 486.) Since my target is 18 generations per second with a 200x200 cellmap on a 20 MHz 386, Listing 17.1 is too slow by a rather wide margin—75 times too slow, in fact. You might say we have a little optimizing to do.</p>
|
||||
|
||||
|
|
@ -173,7 +166,7 @@
|
|||
</tr>
|
||||
</table>
|
||||
|
||||
<h3><a id="Heading6"></a>The Hazards and Advantages of Abstraction</h3>
|
||||
<h3 id="Heading6">The Hazards and Advantages of Abstraction</h3>
|
||||
|
||||
<p>How can we speed up <b>cell_state()</b> and <b>next_generation()</b>? I’ll tell you how <i>not</i> to do it: By writing those member functions in assembly. It’s tempting to say that <b>cell_state()</b> is taking all the time, so we need to speed it up with assembly, but what we really need to do is figure out <i>why</i> <b>cell_state()</b> is taking all the time, then address that aspect of the program directly.</p>
|
||||
|
||||
|
|
@ -206,10 +199,6 @@
|
|||
|
||||
<div align="center">
|
||||
Graphics Programming Black Book © 2001 Michael Abrash
|
||||
</div><!-- all of the reference materials (books) have the footer and subfoot reveresed -->
|
||||
<!-- reference_subfoot = footer -->
|
||||
<!-- reference_footer = subfoot -->
|
||||
<!-- BEGIN SUB FOOTER -->
|
||||
<!-- END FOOTER -->
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
29
17-04.html
29
17-04.html
|
|
@ -10,16 +10,9 @@
|
|||
<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 Game of Life</title><!-- HEADER -->
|
||||
<!-- Empty Reference Subhead -->
|
||||
<!--ISBN=1576101746//-->
|
||||
<!--TITLE=Michael Abrash's Graphics Programming Black Book Special Edition//-->
|
||||
<!--AUTHOR=Michael Abrash//-->
|
||||
<!--PUBLISHER=The Coriolis Group, Inc.//-->
|
||||
<title>Michael Abrash's Graphics Programming Black Book Special Edition: The Game of Life</title>
|
||||
<meta name="chapter" content="17" />
|
||||
<meta name="pages" content="331-334" />
|
||||
<!--UNASSIGNED1//-->
|
||||
<!--UNASSIGNED2//-->
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
|
@ -39,19 +32,17 @@
|
|||
|
||||
<p>There’s a kicker here, though, and that’s the counting of neighbors for cells at the edge of the cellmap. When cellmap wrapping is enabled (so that the cellmap becomes essentially a toroid, with each edge joined seamlessly to the opposite edge, as opposed to having a border of off-cells), neighbors that reside on the other edge of the cellmap can’t be accessed by the standard fixed offset, as shown in Figure 17.1. So, in general, we could improve performance by hard-wiring our neighbor-counting for the bit-per-cell cellmap format, but it seems we’d need a lot of conditional code to handle wrapping, and that would slow things back down again.</p>
|
||||
|
||||
<p><a id="Fig1"><!-- </A><A HREF="javascript:displayWindow('images/17-01.jpg',408,258 )"> --><img src="images/17-01.jpg" /><br />
|
||||
<!-- </A>
|
||||
<BR><A HREF="javascript:displayWindow('images/17-01.jpg',408,258)"> --><b>Figure 17.1</b></a> <i>Edge-wrapping complications.</i></p>
|
||||
<p><a id="Fig1"><img src="images/17-01.jpg" /><br />
|
||||
<b>Figure 17.1</b></a> <i>Edge-wrapping complications.</i></p>
|
||||
|
||||
<p>When a problem doesn’t lend itself well to optimization, make it a practice to see if you can change the problem definition to one that allows for greater efficiency. In this case, we’ll change the problem by putting padding bytes around the edge of the cellmap, and duplicating each edge of the cellmap in the padding bytes at the opposite side, as shown in Figure 17.2. That way, a hard-wired neighbor count will find exactly what it should—the opposite edge—without any special code at all.</p>
|
||||
|
||||
<p>But doesn’t that extra copying of the edges take time? Sure, but only a little; we can build it into the cellmap copying function, and then frankly we won’t even notice it. Avoiding tens or hundreds of thousands of calls to <b>cell_state(),</b> on the other hand, will be <i>very</i> noticeable. Listing 17.3 shows the alterations to Listing 17.1 required to implement a hard-wired neighbor-counting function. This is a minor change, in truth, implemented in about half an hour and not making the code significantly larger—but Listing 17.3 is 3.6 times faster than Listing 17.1, as shown in Table 17.1. We’re up to about 10 generations per second on a 486; not where we want to be, but it is a vast improvement.</p>
|
||||
|
||||
<p><a id="Fig2"><!-- </A><A HREF="javascript:displayWindow('images/17-02.jpg',407,260 )"> --><img src="images/17-02.jpg" /><br />
|
||||
<!-- </A>
|
||||
<BR><A HREF="javascript:displayWindow('images/17-02.jpg',407,260)"> --><b>Figure 17.2</b></a> <i>The “padding cells” solution.</i></p>
|
||||
<p><a id="Fig2"><img src="images/17-02.jpg" /><br />
|
||||
<b>Figure 17.2</b></a> <i>The “padding cells” solution.</i></p>
|
||||
|
||||
<p><b>LISTING 17.3 L17-3.CPP</b></p><!-- CODE //-->
|
||||
<p><b>LISTING 17.3 L17-3.CPP</b></p>
|
||||
<pre>
|
||||
/* cellmap class definition, constructor, copy_cells(), set_cell(),
|
||||
clear_cell(), cell_state(), count_neighbors(), and
|
||||
|
|
@ -214,7 +205,7 @@ void cellmap::next_generation(cellmap& next_map)
|
|||
}
|
||||
}
|
||||
}
|
||||
</pre><!-- END CODE //-->
|
||||
</pre>
|
||||
|
||||
<p><br /></p>
|
||||
|
||||
|
|
@ -233,10 +224,6 @@ void cellmap::next_generation(cellmap& next_map)
|
|||
|
||||
<div align="center">
|
||||
Graphics Programming Black Book © 2001 Michael Abrash
|
||||
</div><!-- all of the reference materials (books) have the footer and subfoot reveresed -->
|
||||
<!-- reference_subfoot = footer -->
|
||||
<!-- reference_footer = subfoot -->
|
||||
<!-- BEGIN SUB FOOTER -->
|
||||
<!-- END FOOTER -->
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
21
17-05.html
21
17-05.html
|
|
@ -10,16 +10,9 @@
|
|||
<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 Game of Life</title><!-- HEADER -->
|
||||
<!-- Empty Reference Subhead -->
|
||||
<!--ISBN=1576101746//-->
|
||||
<!--TITLE=Michael Abrash's Graphics Programming Black Book Special Edition//-->
|
||||
<!--AUTHOR=Michael Abrash//-->
|
||||
<!--PUBLISHER=The Coriolis Group, Inc.//-->
|
||||
<title>Michael Abrash's Graphics Programming Black Book Special Edition: The Game of Life</title>
|
||||
<meta name="chapter" content="17" />
|
||||
<meta name="pages" content="335-338" />
|
||||
<!--UNASSIGNED1//-->
|
||||
<!--UNASSIGNED2//-->
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
|
@ -43,11 +36,11 @@
|
|||
|
||||
<p>Not hardly.</p>
|
||||
|
||||
<h3><a id="Heading7"></a>Heavy-Duty C++ Optimization</h3>
|
||||
<h3 id="Heading7">Heavy-Duty C++ Optimization</h3>
|
||||
|
||||
<p>Before we get to assembly, we still have to perform C<small>++</small> optimization, then see if we can find an alternative approach that better fits the application. It would actually have made much more sense if we had looked for a new approach as our first optimization step, but I decided it would be better to cover straightforward C<small>++</small> optimizations at this point, and the mind-bending stuff a little later. Right now, let’s look at some C<small>++</small> optimizations; Listing 17.4 is a C<small>++</small>-optimized version of Listing 17.3.</p>
|
||||
|
||||
<p><b>LISTING 17.4 L17-4.CPP</b></p><!-- CODE //-->
|
||||
<p><b>LISTING 17.4 L17-4.CPP</b></p>
|
||||
<pre>
|
||||
/* next_generation(), implemented using fast, all-in-one hard-wired
|
||||
neighbor count/update/draw function. Otherwise, the same as
|
||||
|
|
@ -129,7 +122,7 @@ neighbor_count++;
|
|||
row_cell_ptr += width_in_bytes; // point to start of next row
|
||||
}
|
||||
}
|
||||
</pre><!-- END CODE //-->
|
||||
</pre>
|
||||
|
||||
<p>Listing 17.4 and Listing 17.3 are functionally the same; the only difference lies in how <b>next_generation()</b> is implemented. (Only <b>next_generation()</b> is shown in Listing 17.4; the program is otherwise identical to Listing 17.3.) Listing 17.4 applies the following optimizations to <b>next_generation()</b>:</p>
|
||||
|
||||
|
|
@ -164,10 +157,6 @@ neighbor_count++;
|
|||
|
||||
<div align="center">
|
||||
Graphics Programming Black Book © 2001 Michael Abrash
|
||||
</div><!-- all of the reference materials (books) have the footer and subfoot reveresed -->
|
||||
<!-- reference_subfoot = footer -->
|
||||
<!-- reference_footer = subfoot -->
|
||||
<!-- BEGIN SUB FOOTER -->
|
||||
<!-- END FOOTER -->
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
26
17-06.html
26
17-06.html
|
|
@ -10,16 +10,9 @@
|
|||
<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 Game of Life</title><!-- HEADER -->
|
||||
<!-- Empty Reference Subhead -->
|
||||
<!--ISBN=1576101746//-->
|
||||
<!--TITLE=Michael Abrash's Graphics Programming Black Book Special Edition//-->
|
||||
<!--AUTHOR=Michael Abrash//-->
|
||||
<!--PUBLISHER=The Coriolis Group, Inc.//-->
|
||||
<title>Michael Abrash's Graphics Programming Black Book Special Edition: The Game of Life</title>
|
||||
<meta name="chapter" content="17" />
|
||||
<meta name="pages" content="338-340" />
|
||||
<!--UNASSIGNED1//-->
|
||||
<!--UNASSIGNED2//-->
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
|
@ -49,7 +42,7 @@
|
|||
<li>Cells change state relatively infrequently.</li>
|
||||
</ul>
|
||||
|
||||
<h3><a id="Heading8"></a>Bringing In the Right Brain</h3>
|
||||
<h3 id="Heading8">Bringing In the Right Brain</h3>
|
||||
|
||||
<p>In the previous section, we saw how a C<small>++</small> program could be sped up about eight times simply by rearranging the data and code in straightforward ways. Now we’re going to see how right-brain non-linear optimization can speed things up by another four times—and make the code <i>simpler.</i></p>
|
||||
|
||||
|
|
@ -57,7 +50,7 @@
|
|||
|
||||
<p>I have two objectives to achieve in the remainder of this chapter. First, I want to show that optimization consists of many levels, from assembly language up to conceptual design, and that assembly language kicks in pretty late in the optimization process. Second, I want to encourage you to saturate your brain with everything you know about any particular optimization problem, then make space for your right brain to solve the problem.</p>
|
||||
|
||||
<h4 align="left"><a id="Heading9"></a>Re-Examining the Task</h4>
|
||||
<h4 align="left" id="Heading9">Re-Examining the Task</h4>
|
||||
|
||||
<p>Earlier in this chapter, we looked at a straightforward Game of Life implementation, then increased performance considerably by making the implementation a little less abstract and a little less general. We made a small change to the cellmap format, adding padding bytes off the edges so that pointer arithmetic would always work, but the major optimizations were moving the critical code into a single loop and using pointers rather than member functions whenever possible. In other words, we took what we already knew and made it more efficient.</p>
|
||||
|
||||
|
|
@ -79,11 +72,10 @@
|
|||
|
||||
<p><i>Know your data.</i></p>
|
||||
|
||||
<p><a id="Fig3"><!-- </A><A HREF="javascript:displayWindow('images/17-03.jpg',405,113 )"> --><img src="images/17-03.jpg" /><br />
|
||||
<!-- </A>
|
||||
<BR><A HREF="javascript:displayWindow('images/17-03.jpg',405,113)"> --><b>Figure 17.3</b></a> <i>New cell format.</i></p>
|
||||
<p><a id="Fig3"><img src="images/17-03.jpg" /><br />
|
||||
<b>Figure 17.3</b></a> <i>New cell format.</i></p>
|
||||
|
||||
<h4 align="left"><a id="Heading10"></a>Acting on What We Know</h4>
|
||||
<h4 align="left" id="Heading10">Acting on What We Know</h4>
|
||||
|
||||
<p>Once we’ve changed the cellmap format to store neighbor counts as well as states, with a byte for each cell, we can get another performance boost by again examining what we know about our data. I said earlier that most cells are off during any given generation. This means that most cells have no neighbors that are on. Since the cell map representation for an off-cell that has no neighbors is a zero byte, we can skip over scads of unchanged cells at a pop simply by scanning for non-zero bytes. This is much faster than explicitly testing cell states and neighbor counts, and lends itself beautifully to assembly language implementation as <b>REPZ SCASB</b> or (with a little cleverness) <b>REPZ SCASW.</b> (Unfortunately, there’s no C library function that can scan memory for the next byte that’s non-zero.)</p>
|
||||
|
||||
|
|
@ -106,10 +98,6 @@
|
|||
|
||||
<div align="center">
|
||||
Graphics Programming Black Book © 2001 Michael Abrash
|
||||
</div><!-- all of the reference materials (books) have the footer and subfoot reveresed -->
|
||||
<!-- reference_subfoot = footer -->
|
||||
<!-- reference_footer = subfoot -->
|
||||
<!-- BEGIN SUB FOOTER -->
|
||||
<!-- END FOOTER -->
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
19
17-07.html
19
17-07.html
|
|
@ -10,16 +10,9 @@
|
|||
<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 Game of Life</title><!-- HEADER -->
|
||||
<!-- Empty Reference Subhead -->
|
||||
<!--ISBN=1576101746//-->
|
||||
<!--TITLE=Michael Abrash's Graphics Programming Black Book Special Edition//-->
|
||||
<!--AUTHOR=Michael Abrash//-->
|
||||
<!--PUBLISHER=The Coriolis Group, Inc.//-->
|
||||
<title>Michael Abrash's Graphics Programming Black Book Special Edition: The Game of Life</title>
|
||||
<meta name="chapter" content="17" />
|
||||
<meta name="pages" content="340-345" />
|
||||
<!--UNASSIGNED1//-->
|
||||
<!--UNASSIGNED2//-->
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
|
@ -37,7 +30,7 @@
|
|||
|
||||
<p><br /></p>
|
||||
|
||||
<p><b>LISTING 17.5 L17-5.CPP</b></p><!-- CODE //-->
|
||||
<p><b>LISTING 17.5 L17-5.CPP</b></p>
|
||||
<pre>
|
||||
/* C++ Game of Life implementation for any mode for which mode set
|
||||
and draw pixel functions can be provided. The cellmap stores the
|
||||
|
|
@ -312,7 +305,7 @@ void cellmap::init()
|
|||
}
|
||||
} while (—init_length);
|
||||
}
|
||||
</pre><!-- END CODE //-->
|
||||
</pre>
|
||||
|
||||
<p><br /></p>
|
||||
|
||||
|
|
@ -331,10 +324,6 @@ void cellmap::init()
|
|||
|
||||
<div align="center">
|
||||
Graphics Programming Black Book © 2001 Michael Abrash
|
||||
</div><!-- all of the reference materials (books) have the footer and subfoot reveresed -->
|
||||
<!-- reference_subfoot = footer -->
|
||||
<!-- reference_footer = subfoot -->
|
||||
<!-- BEGIN SUB FOOTER -->
|
||||
<!-- END FOOTER -->
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
17
17-08.html
17
17-08.html
|
|
@ -10,16 +10,9 @@
|
|||
<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 Game of Life</title><!-- HEADER -->
|
||||
<!-- Empty Reference Subhead -->
|
||||
<!--ISBN=1576101746//-->
|
||||
<!--TITLE=Michael Abrash's Graphics Programming Black Book Special Edition//-->
|
||||
<!--AUTHOR=Michael Abrash//-->
|
||||
<!--PUBLISHER=The Coriolis Group, Inc.//-->
|
||||
<title>Michael Abrash's Graphics Programming Black Book Special Edition: The Game of Life</title>
|
||||
<meta name="chapter" content="17" />
|
||||
<meta name="pages" content="345-346" />
|
||||
<!--UNASSIGNED1//-->
|
||||
<!--UNASSIGNED2//-->
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
|
@ -55,7 +48,7 @@
|
|||
|
||||
<p>No doubt we could get another two to five times improvement with good assembly code—but that’s dwarfed by a 30-times improvement, so optimization at a conceptual level <i>must</i> come first.</p>
|
||||
|
||||
<h4 align="left"><a id="Heading11"></a>The Challenge That Ate My Life</h4>
|
||||
<h4 align="left" id="Heading11">The Challenge That Ate My Life</h4>
|
||||
|
||||
<p>The most recent optimization challenge I laid my community of readers was to write the fastest possible Game of Life generation engine. By “engine” I meant that I didn’t care about time spent in input or output, only time consumed by the call to <b>next-generation.</b> The time spent updating the cellmap was what I wanted people to concentrate on.</p>
|
||||
|
||||
|
|
@ -96,10 +89,6 @@
|
|||
|
||||
<div align="center">
|
||||
Graphics Programming Black Book © 2001 Michael Abrash
|
||||
</div><!-- all of the reference materials (books) have the footer and subfoot reveresed -->
|
||||
<!-- reference_subfoot = footer -->
|
||||
<!-- reference_footer = subfoot -->
|
||||
<!-- BEGIN SUB FOOTER -->
|
||||
<!-- END FOOTER -->
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
21
18-01.html
21
18-01.html
|
|
@ -10,16 +10,9 @@
|
|||
<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: It's a Wonderful Life</title><!-- HEADER -->
|
||||
<!-- Empty Reference Subhead -->
|
||||
<!--ISBN=1576101746//-->
|
||||
<!--TITLE=Michael Abrash's Graphics Programming Black Book Special Edition//-->
|
||||
<!--AUTHOR=Michael Abrash//-->
|
||||
<!--PUBLISHER=The Coriolis Group, Inc.//-->
|
||||
<title>Michael Abrash's Graphics Programming Black Book Special Edition: It's a Wonderful Life</title>
|
||||
<meta name="chapter" content="18" />
|
||||
<meta name="pages" content="347-351" />
|
||||
<!--UNASSIGNED1//-->
|
||||
<!--UNASSIGNED2//-->
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
|
@ -37,10 +30,10 @@
|
|||
|
||||
<p><br /></p>
|
||||
|
||||
<h2><a id="Heading1"></a>Chapter 18<br />
|
||||
<h2 id="Heading1">Chapter 18<br />
|
||||
It’s a plain Wonderful Life</h2>
|
||||
|
||||
<h3><a id="Heading2"></a>Optimization beyond the Pale</h3>
|
||||
<h3 id="Heading2">Optimization beyond the Pale</h3>
|
||||
|
||||
<p>When I was in high school, my gym teacher had us run a race around the soccer field, or rather, around a course marked with cones that roughly outlined the shape of the field. I quickly settled into second place behind Dwight Chamberlin. We cruised around the field, and when we came to the far corner, Dwight cut across the corner, inside a cone placed awkwardly far out from the others. I followed, and everyone else cut inside the cone too—except the pear-shaped kid bringing up the rear, who plodded his way around every single cone on his way to finishing about half a lap behind. When the laggard finally crossed the finish line, the coach named him the winner, to my considerable irritation. After all, the object was to see who could run the fastest, wasn’t it?</p>
|
||||
|
||||
|
|
@ -56,7 +49,7 @@
|
|||
</tr>
|
||||
</table>
|
||||
|
||||
<h3><a id="Heading3"></a>Breaking the Rules</h3>
|
||||
<h3 id="Heading3">Breaking the Rules</h3>
|
||||
|
||||
<p>The other reason for the anecdote has to do with the way my second Optimization Challenge worked itself out. If you’ll recall from the last chapter, the challenge I made to the readers of <i>PC TECHNIQUES</i> was to devise the fastest possible version of the Game of Life cellular automata simulation game. I gave an example, laid out the rules, and stood aside. Good thing, too. <i>Apres moi, le deluge....</i></p>
|
||||
|
||||
|
|
@ -87,10 +80,6 @@
|
|||
|
||||
<div align="center">
|
||||
Graphics Programming Black Book © 2001 Michael Abrash
|
||||
</div><!-- all of the reference materials (books) have the footer and subfoot reveresed -->
|
||||
<!-- reference_subfoot = footer -->
|
||||
<!-- reference_footer = subfoot -->
|
||||
<!-- BEGIN SUB FOOTER -->
|
||||
<!-- END FOOTER -->
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue