Convert notes into blockquotes
This commit is contained in:
parent
832fb15e54
commit
8ed9dea394
105 changed files with 884 additions and 411 deletions
7
01-01.md
7
01-01.md
|
|
@ -85,9 +85,10 @@ solid programming skills, preferably using an optimizing compiler or
|
||||||
assembly language. The optimization at the end is just the finishing
|
assembly language. The optimization at the end is just the finishing
|
||||||
touch, however.
|
touch, however.
|
||||||
|
|
||||||
------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
> 
|
||||||
 *Without good design, good algorithms, and complete understanding of the program's operation, your carefully optimized code will amount to one of mankind's least fruitful creations—a fast slow program*.
|
> Without good design, good algorithms, and complete understanding of the
|
||||||
------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
> program's operation, your carefully optimized code will amount to one of
|
||||||
|
> mankind's least fruitful creations—a fast slow program.
|
||||||
|
|
||||||
"What's a fast slow program?" you ask. That's a good question, and a
|
"What's a fast slow program?" you ask. That's a good question, and a
|
||||||
brief (true) story is perhaps the best answer.
|
brief (true) story is perhaps the best answer.
|
||||||
|
|
|
||||||
6
01-02.md
6
01-02.md
|
|
@ -131,9 +131,9 @@ 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
|
same, however, and all are much too slow to be acceptable. Listing 1.1
|
||||||
requires over two and one-half minutes to checksum *one* file!
|
requires over two and one-half minutes to checksum *one* file!
|
||||||
|
|
||||||
------------------- -----------------------------------------------------------------------------------------------------------------------------------------------
|
> 
|
||||||
 *Listings 1.2 and 1.3 form the C/assembly equivalent to Listing 1.1, and Listings 1.6 and 1.7 form the C/assembly equivalent to Listing 1.5.*
|
> Listings 1.2 and 1.3 form the C/assembly equivalent to Listing 1.1, and
|
||||||
------------------- -----------------------------------------------------------------------------------------------------------------------------------------------
|
> Listings 1.6 and 1.7 form the C/assembly equivalent to Listing 1.5.
|
||||||
|
|
||||||
These results make it clear that it's folly to rely on your compiler's
|
These results make it clear that it's folly to rely on your compiler's
|
||||||
optimization to make your programs fast. Listing 1.1 is simply poorly
|
optimization to make your programs fast. Listing 1.1 is simply poorly
|
||||||
|
|
|
||||||
17
01-04.md
17
01-04.md
|
|
@ -10,9 +10,10 @@ chapter: '01'
|
||||||
pages: 013-015
|
pages: 013-015
|
||||||
---
|
---
|
||||||
|
|
||||||
------------------- --------------------------------------------------------------------------------------------------------------------------------------------------
|
> 
|
||||||
 *Make sure you understand what really goes on when you insert a seemingly-innocuous function call into the time-critical portions of your code.*
|
> Make sure you understand what really goes on when you insert a
|
||||||
------------------- --------------------------------------------------------------------------------------------------------------------------------------------------
|
> seemingly-innocuous function call into the time-critical portions of
|
||||||
|
> your code.
|
||||||
|
|
||||||
In this case that means knowing how DOS and the C/C++ file-access
|
In this case that means knowing how DOS and the C/C++ file-access
|
||||||
libraries do their work. In other words, *know the territory*!
|
libraries do their work. In other words, *know the territory*!
|
||||||
|
|
@ -74,9 +75,13 @@ that handle the time-critical operations, but C is still used for
|
||||||
checking command-line parameters, operning files, printing, and the
|
checking command-line parameters, operning files, printing, and the
|
||||||
like.
|
like.
|
||||||
|
|
||||||
------------------- --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
> 
|
||||||
 *If you were to implement any of the listings in this chapter entirely in hand-optimized assembly, I suppose you might get a performance improvement of a few percent—but I rather doubt you'd get even that much, and you'd sure as heck spend an awful lot of time for whatever meager improvement does result. Let C do what it does well, and use assembly only when it makes a perceptible difference.*
|
> If you were to implement any of the listings in this chapter entirely in
|
||||||
------------------- --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
> hand-optimized assembly, I suppose you might get a performance
|
||||||
|
> improvement of a few percent—but I rather doubt you'd get even that
|
||||||
|
> much, and you'd sure as heck spend an awful lot of time for whatever
|
||||||
|
> meager improvement does result. Let C do what it does well, and use
|
||||||
|
> assembly only when it makes a perceptible difference.
|
||||||
|
|
||||||
Besides, we don't want to optimize until the design is refined to our
|
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
|
satisfaction, and that won't be the case until we've thought about other
|
||||||
|
|
|
||||||
9
01-05.md
9
01-05.md
|
|
@ -21,9 +21,12 @@ Table 1.1 shows that Listing 1.5 is 2.5 to 4 times faster than Listing
|
||||||
1.4 (and as much as 49 times faster than Listing 1.1!), even though it
|
1.4 (and as much as 49 times faster than Listing 1.1!), even though it
|
||||||
uses no assembly at all.
|
uses no assembly at all.
|
||||||
|
|
||||||
------------------- -----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
> 
|
||||||
 *Clearly, you can do well by using special-purpose C code in place of a C library function—if you have a thorough understanding of how the C library function operates and exactly what your application needs done. Otherwise, you'll end up rewriting C library functions in C, which makes no sense at all.*
|
> Clearly, you can do well by using special-purpose C code in place of a C
|
||||||
------------------- -----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
> library function—if you have a thorough understanding of how the C
|
||||||
|
> library function operates and exactly what your application needs done.
|
||||||
|
> Otherwise, you'll end up rewriting C library functions in C, which makes
|
||||||
|
> no sense at all.
|
||||||
|
|
||||||
**LISTING 1.5 L1-5.C**
|
**LISTING 1.5 L1-5.C**
|
||||||
|
|
||||||
|
|
|
||||||
6
01-06.md
6
01-06.md
|
|
@ -81,9 +81,9 @@ instructions and/or unrolled loops can be used effectively, assembly
|
||||||
tends to be considerably faster relative to C than it is in this very
|
tends to be considerably faster relative to C than it is in this very
|
||||||
specific case.
|
specific case.
|
||||||
|
|
||||||
------------------- -----------------------------------------------------------------------------------------------------------
|
> 
|
||||||
 *Don't get hung up on optimizing compilers or assembly language—the best optimizer is between your ears.*
|
> Don't get hung up on optimizing compilers or assembly language—the best
|
||||||
------------------- -----------------------------------------------------------------------------------------------------------
|
> optimizer is between your ears.
|
||||||
|
|
||||||
All this is basically a way of saying: Know where you're going, know the
|
All this is basically a way of saying: Know where you're going, know the
|
||||||
territory, and know when it matters.
|
territory, and know when it matters.
|
||||||
|
|
|
||||||
6
02-01.md
6
02-01.md
|
|
@ -62,9 +62,9 @@ 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
|
multibit rotation was being performed every time through the loop, for a
|
||||||
total of four separate time-consuming multibit rotations!
|
total of four separate time-consuming multibit rotations!
|
||||||
|
|
||||||
------------------- -----------------------------------------------------------------------------------------------------------------------------------------------
|
> 
|
||||||
 *While the instructions themselves were individually optimized, the overall approach did not make the best possible use of the instructions.*
|
> While the instructions themselves were individually optimized, the
|
||||||
------------------- -----------------------------------------------------------------------------------------------------------------------------------------------
|
> overall approach did not make the best possible use of the instructions.
|
||||||
|
|
||||||
I changed the code to the following:
|
I changed the code to the following:
|
||||||
|
|
||||||
|
|
|
||||||
6
02-02.md
6
02-02.md
|
|
@ -105,6 +105,6 @@ knowledge about programming them effectively is by far the hardest
|
||||||
knowledge to gather. A good portion of this book is devoted to seeking
|
knowledge to gather. A good portion of this book is devoted to seeking
|
||||||
out such knowledge.
|
out such knowledge.
|
||||||
|
|
||||||
------------------- ------------------------------------------------------------------------------------------------------------------------------
|
> 
|
||||||
 *Be forewarned, though: No matter how much you learn about programming the PC in assembly, there's always more to discover.*
|
> Be forewarned, though: No matter how much you learn about programming
|
||||||
------------------- ------------------------------------------------------------------------------------------------------------------------------
|
> the PC in assembly, there's always more to discover.
|
||||||
9
02-03.md
9
02-03.md
|
|
@ -43,9 +43,12 @@ reusability, source code control, choice of development environment, and
|
||||||
the like that they often forget rule \#1: From the user's perspective,
|
the like that they often forget rule \#1: From the user's perspective,
|
||||||
*performance is fundamental*.
|
*performance is fundamental*.
|
||||||
|
|
||||||
------------------- ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
> 
|
||||||
 *Comment your code, design it carefully, and write non-time-critical portions in a high-level language, if you wish—but when you write the portions that interact with the user and/or affect response time, performance must be your paramount objective, and assembly is the path to that goal*.
|
> Comment your code, design it carefully, and write non-time-critical
|
||||||
------------------- ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
> portions in a high-level language, if you wish—but when you write the
|
||||||
|
> portions that interact with the user and/or affect response time,
|
||||||
|
> performance must be your paramount objective, and assembly is the path
|
||||||
|
> to that goal.
|
||||||
|
|
||||||
Knowledge of the sort described earlier is absolutely essential to
|
Knowledge of the sort described earlier is absolutely essential to
|
||||||
fulfilling either of the objectives of assembly programming. What that
|
fulfilling either of the objectives of assembly programming. What that
|
||||||
|
|
|
||||||
10
03-01.md
10
03-01.md
|
|
@ -57,9 +57,13 @@ performance of his code and found it to be unexpectedly slow, curiosity
|
||||||
might well have led him to experiment further and thereby add to his
|
might well have led him to experiment further and thereby add to his
|
||||||
store of reliable information about the CPU.
|
store of reliable information about the CPU.
|
||||||
|
|
||||||
------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
> 
|
||||||
 *There you have an important tenet of assembly language optimization: After crafting the best code possible, check it in action to see if it's really doing what you think it is. If it's not behaving as expected, that's all to the good, since solving mysteries is the path to knowledge. You'll learn more in this way, I assure you, than from any manual or book on assembly language.*
|
> There you have an important tenet of assembly language optimization:
|
||||||
------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
> After crafting the best code possible, check it in action to see if it's
|
||||||
|
> really doing what you think it is. If it's not behaving as expected,
|
||||||
|
> that's all to the good, since solving mysteries is the path to
|
||||||
|
> knowledge. You'll learn more in this way, I assure you, than from any
|
||||||
|
> manual or book on assembly language.
|
||||||
|
|
||||||
*Assume nothing*. I cannot emphasize this strongly enough—when you care
|
*Assume nothing*. I cannot emphasize this strongly enough—when you care
|
||||||
about performance, do your best to improve the code and then *measure*
|
about performance, do your best to improve the code and then *measure*
|
||||||
|
|
|
||||||
9
04-04.md
9
04-04.md
|
|
@ -38,9 +38,12 @@ depending on the code mix preceding that instruction.* Similarly, the
|
||||||
state in which a given instruction leaves the prefetch queue affects the
|
state in which a given instruction leaves the prefetch queue affects the
|
||||||
overall execution time of the following instructions.
|
overall execution time of the following instructions.
|
||||||
|
|
||||||
------------------- ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
> 
|
||||||
 *In other words, while the execution time for a given instruction is constant, the fetch time for that instruction depends heavily on the context in which the instruction is executing—the amount of prefetching the preceding instructions allowed—and can vary from a full 4 cycles per instruction byte to no time at all.*
|
> In other words, while the execution time for a given instruction is
|
||||||
------------------- ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
> constant, the fetch time for that instruction depends heavily on the
|
||||||
|
> context in which the instruction is executing—the amount of prefetching
|
||||||
|
> the preceding instructions allowed—and can vary from a full 4 cycles per
|
||||||
|
> instruction byte to no time at all.
|
||||||
|
|
||||||
As we'll see later, other cycle-eaters, such as DRAM refresh and display
|
As we'll see later, other cycle-eaters, such as DRAM refresh and display
|
||||||
memory wait states, can cause prefetching variations even during
|
memory wait states, can cause prefetching variations even during
|
||||||
|
|
|
||||||
6
04-05.md
6
04-05.md
|
|
@ -83,9 +83,9 @@ in the Intel manuals, but may take as much as 4 cycles per byte longer,
|
||||||
depending on the state of the prefetch queue when the preceding
|
depending on the state of the prefetch queue when the preceding
|
||||||
instruction ends.
|
instruction ends.
|
||||||
|
|
||||||
------------------- ----------------------------------------------------------------------------------------------------------------------------------------------
|
> 
|
||||||
 *The only true execution time for an instruction is a time measured in a certain context, and that time is meaningful only in that context.*
|
> The only true execution time for an instruction is a time measured in a
|
||||||
------------------- ----------------------------------------------------------------------------------------------------------------------------------------------
|
> certain context, and that time is meaningful only in that context.
|
||||||
|
|
||||||
What we *really* want is to know how long useful working code takes to
|
What we *really* 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
|
run, not how long a single instruction takes, and the Zen timer gives us
|
||||||
|
|
|
||||||
18
04-10.md
18
04-10.md
|
|
@ -10,9 +10,14 @@ chapter: '04'
|
||||||
pages: 106-109
|
pages: 106-109
|
||||||
---
|
---
|
||||||
|
|
||||||
------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
> 
|
||||||
 *A line-drawing subroutine, which executes perhaps a dozen instructions for each display memory access, generally loses less performance to the display adapter cycle-eater than does a block-copy or scrolling subroutine that uses **REP MOVS** instructions. Scaled and three-dimensional graphics, which spend a great deal of time performing calculations (often using very slow floating-point arithmetic), tend to suffer less.*
|
> A line-drawing subroutine, which executes perhaps a dozen instructions
|
||||||
------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
> for each display memory access, generally loses less performance to the
|
||||||
|
> display adapter cycle-eater than does a block-copy or scrolling
|
||||||
|
> subroutine that uses **REP MOVS** instructions. Scaled and
|
||||||
|
> three-dimensional graphics, which spend a great deal of time performing
|
||||||
|
> calculations (often using very slow floating-point arithmetic), tend to
|
||||||
|
> suffer less.
|
||||||
|
|
||||||
In addition, code that accesses display memory infrequently tends to
|
In addition, code that accesses display memory infrequently tends to
|
||||||
suffer only about half of the maximum display memory wait states,
|
suffer only about half of the maximum display memory wait states,
|
||||||
|
|
@ -48,9 +53,10 @@ 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
|
many display memory accesses means half as many display memory wait
|
||||||
states.
|
states.
|
||||||
|
|
||||||
------------------- --------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
> 
|
||||||
 *Moreover, 486s and Pentiums, as well as recent Super VGAs, employ write-caching schemes that make display memory writes considerably faster than display memory reads.*
|
> Moreover, 486s and Pentiums, as well as recent Super VGAs, employ
|
||||||
------------------- --------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
> write-caching schemes that make display memory writes considerably
|
||||||
|
> faster than display memory reads.
|
||||||
|
|
||||||
Along the same line, the display adapter cycle-eater makes the popular
|
Along the same line, the display adapter cycle-eater makes the popular
|
||||||
exclusive-OR animation technique, which requires paired reads and writes
|
exclusive-OR animation technique, which requires paired reads and writes
|
||||||
|
|
|
||||||
7
05-01.md
7
05-01.md
|
|
@ -58,9 +58,10 @@ the overhead of driving back and forth made for miserable performance.
|
||||||
Renting a truck (the restartable block approach) would have required
|
Renting a truck (the restartable block approach) would have required
|
||||||
more effort and forethought, but would have paid off handsomely.
|
more effort and forethought, but would have paid off handsomely.
|
||||||
|
|
||||||
------------------- ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
> 
|
||||||
 *The easy, familiar approach often has nothing in its favor except that it requires less thinking; not a great virtue when writing high-performance code—or when moving.*
|
> The easy, familiar approach often has nothing in its favor except that
|
||||||
------------------- ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
> it requires less thinking; not a great virtue when writing
|
||||||
|
> high-performance code—or when moving.
|
||||||
|
|
||||||
And with that, let's look at a fairly complex application of restartable
|
And with that, let's look at a fairly complex application of restartable
|
||||||
blocks.
|
blocks.
|
||||||
|
|
|
||||||
8
05-02.md
8
05-02.md
|
|
@ -18,9 +18,11 @@ exactly how long our search buffer is. Even if a given **strstr()**
|
||||||
implementation is well-written, its performance will suffer, at least
|
implementation is well-written, its performance will suffer, at least
|
||||||
for our application, from unnecessary overhead.
|
for our application, from unnecessary overhead.
|
||||||
|
|
||||||
------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
> 
|
||||||
 *This illustrates why you shouldn't think of C/C++ library functions as black boxes; understand what they do and try to figure out how they do it, and relate that to their performance in the context you're interested in.*
|
> This illustrates why you shouldn't think of C/C++ library functions as
|
||||||
------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
> black boxes; understand what they do and try to figure out how they do
|
||||||
|
> it, and relate that to their performance in the context you're
|
||||||
|
> interested in.
|
||||||
|
|
||||||
### Brute-Force Techniques {#Heading5}
|
### Brute-Force Techniques {#Heading5}
|
||||||
|
|
||||||
|
|
|
||||||
16
05-05.md
16
05-05.md
|
|
@ -22,9 +22,11 @@ And so we find, as we so often will, that optimization is definitely not
|
||||||
a cut-and-dried matter, and that there is no such thing as a single
|
a cut-and-dried matter, and that there is no such thing as a single
|
||||||
"best" approach.
|
"best" approach.
|
||||||
|
|
||||||
------------------- --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
> 
|
||||||
 *You must know what your application will typically do, and you must know whether you're more concerned with average or worst-case performance before you can decide how best to speed up your program—and, indeed, whether speeding it up is worth doing at all.*
|
> You must know what your application will typically do, and you must know
|
||||||
------------------- --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
> whether you're more concerned with average or worst-case performance
|
||||||
|
> before you can decide how best to speed up your program—and, indeed,
|
||||||
|
> whether speeding it up is worth doing at all.
|
||||||
|
|
||||||
By the way, don't think that just because very large block sizes don't
|
By the way, don't think that just because very large block sizes don't
|
||||||
much improve performance, it wasn't worth using restartable blocks in
|
much improve performance, it wasn't worth using restartable blocks in
|
||||||
|
|
@ -56,9 +58,11 @@ to the **read()** function, but a code profiler could be used to do the
|
||||||
same thing much more easily), I found that the best code in the world
|
same thing much more easily), I found that the best code in the world
|
||||||
wouldn't make much difference.
|
wouldn't make much difference.
|
||||||
|
|
||||||
------------------- -----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
> 
|
||||||
 *When you try to speed up code, take a moment to identify the hot spots in your program so that you know where optimization is needed and whether it will make a significant difference before you invest your time.*
|
> When you try to speed up code, take a moment to identify the hot spots
|
||||||
------------------- -----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
> in your program so that you know where optimization is needed and
|
||||||
|
> whether it will make a significant difference before you invest your
|
||||||
|
> time.
|
||||||
|
|
||||||
As for restartable blocks: Here we tackled a considerably more complex
|
As for restartable blocks: Here we tackled a considerably more complex
|
||||||
application of restartable blocks than we did in Chapter 1—which turned
|
application of restartable blocks than we did in Chapter 1—which turned
|
||||||
|
|
|
||||||
8
06-02.md
8
06-02.md
|
|
@ -173,9 +173,11 @@ Multiplying a 32-bit value by a non-power-of-two multiplier in just 2
|
||||||
cycles is a pretty neat trick, even though it works only on a 386 or
|
cycles is a pretty neat trick, even though it works only on a 386 or
|
||||||
486.
|
486.
|
||||||
|
|
||||||
------------------- ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
> 
|
||||||
 *The full list of values that **LEA** can multiply a register by on a 386 or 486 is: 2, 3, 4, 5, 8, and 9. That list doesn't include every multiplier you might want, but it covers some commonly used ones, and the performance is hard to beat.*
|
> The full list of values that **LEA** can multiply a register by on a 386
|
||||||
------------------- ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
> or 486 is: 2, 3, 4, 5, 8, and 9. That list doesn't include every
|
||||||
|
> multiplier you might want, but it covers some commonly used ones, and
|
||||||
|
> the performance is hard to beat.
|
||||||
|
|
||||||
I'd like to extend my thanks to Duane Strong of Metagraphics for his
|
I'd like to extend my thanks to Duane Strong of Metagraphics for his
|
||||||
help in brainstorming uses for the 386 version of **LEA** and for
|
help in brainstorming uses for the 386 version of **LEA** and for
|
||||||
|
|
|
||||||
24
07-01.md
24
07-01.md
|
|
@ -68,9 +68,11 @@ algorithm selection and good design are fundamental to performance. The
|
||||||
extra horsepower a superb assembly language implementation gives a
|
extra horsepower a superb assembly language implementation gives a
|
||||||
program is worth bothering with only in the context of a good design.
|
program is worth bothering with only in the context of a good design.
|
||||||
|
|
||||||
------------------- ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
> 
|
||||||
 *Assembly language optimization is a small but crucial corner of the PC programming world. Use it sparingly and only within the framework of a good design—but ignore it and you may find various portions of your anatomy out in the cold.*
|
> Assembly language optimization is a small but crucial corner of the PC
|
||||||
------------------- ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
> programming world. Use it sparingly and only within the framework of a
|
||||||
|
> good design—but ignore it and you may find various portions of your
|
||||||
|
> anatomy out in the cold.
|
||||||
|
|
||||||
So, drawing fortitude from the knowledge that our quest is a pure and
|
So, drawing fortitude from the knowledge that our quest is a pure and
|
||||||
worthy one, let's resume our exploration of assembly language
|
worthy one, let's resume our exploration of assembly language
|
||||||
|
|
@ -96,9 +98,11 @@ instinctively stuffs the loop count in CX and reaches for **LOOP** when
|
||||||
setting up a loop. That's fine—**LOOP** does, of course, work as
|
setting up a loop. That's fine—**LOOP** does, of course, work as
|
||||||
advertised—but there is one problem:
|
advertised—but there is one problem:
|
||||||
|
|
||||||
------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
> 
|
||||||
 *On half of the processors in the x86 family, **LOOP** is slower than **DEC CX** followed by **JNZ**. (Granted, **DEC CX/JNZ** isn't precisely equivalent to **LOOP,** because **DEC** alters the flags and LOOP doesn't, but in most situations they're comparable.)*
|
> On half of the processors in the x86 family, **LOOP** is slower than
|
||||||
------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
> **DEC CX** followed by **JNZ**. (Granted, **DEC CX/JNZ** isn't precisely
|
||||||
|
> equivalent to **LOOP,** because **DEC** alters the flags and LOOP
|
||||||
|
> doesn't, but in most situations they're comparable.)
|
||||||
|
|
||||||
How can this be? Don't ask me, ask Intel. On the 8088 and 80286,
|
How can this be? Don't ask me, ask Intel. On the 8088 and 80286,
|
||||||
**LOOP** is indeed faster than **DEC CX/JNZ** by a cycle, and **LOOP**
|
**LOOP** is indeed faster than **DEC CX/JNZ** by a cycle, and **LOOP**
|
||||||
|
|
@ -116,6 +120,8 @@ Pentium as well. (By the way, all this is not just theory; I've timed
|
||||||
the relative performances of **LOOP** and **DEC CX/JNZ** on a cached
|
the relative performances of **LOOP** and **DEC CX/JNZ** on a cached
|
||||||
386, and LOOP really is slower.)
|
386, and LOOP really is slower.)
|
||||||
|
|
||||||
------------------- --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
> 
|
||||||
 *Things are stranger still for **LOOP**'s relative **JCXZ,** which branches if and only if CX is zero. **JCXZ** is faster than **AND CX,CX/JZ** on the 8088 and 80286, and equivalent on the 80386—but is about twice as slow on the 486!*
|
> Things are stranger still for **LOOP**'s relative **JCXZ,** which
|
||||||
------------------- --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
> branches if and only if CX is zero. **JCXZ** is faster than **AND
|
||||||
|
> CX,CX/JZ** on the 8088 and 80286, and equivalent on the 80386—but is
|
||||||
|
> about twice as slow on the 486!
|
||||||
9
07-02.md
9
07-02.md
|
|
@ -86,9 +86,12 @@ checking whether it's zero. There may be ways to perform those tasks a
|
||||||
little faster by selecting different instructions, but they can get only
|
little faster by selecting different instructions, but they can get only
|
||||||
so fast, and branching can't even get all that fast.
|
so fast, and branching can't even get all that fast.
|
||||||
|
|
||||||
------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
> 
|
||||||
 *The trick, then, is not to find the fastest way to decrement a count and branch conditionally, but rather to figure out how to accomplish the same result without decrementing or branching as often. Remember the Kobiyashi Maru problem in* Star Trek*?The same principle applies here: Redefine the problem to one that offers better solutions.*
|
> The trick, then, is not to find the fastest way to decrement a count and
|
||||||
------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
> branch conditionally, but rather to figure out how to accomplish the
|
||||||
|
> same result without decrementing or branching as often. Remember the
|
||||||
|
> Kobiyashi Maru problem in *Star Trek*? The same principle applies here:
|
||||||
|
> Redefine the problem to one that offers better solutions.
|
||||||
|
|
||||||
Consider Listing 7.1, which searches a buffer until either the specified
|
Consider Listing 7.1, which searches a buffer until either the specified
|
||||||
byte is found, a zero byte is found, or the specified number of
|
byte is found, a zero byte is found, or the specified number of
|
||||||
|
|
|
||||||
11
07-04.md
11
07-04.md
|
|
@ -151,6 +151,11 @@ that algorithmic improvements can produce, but it can get you a critical
|
||||||
50 percent or 100 percent improvement when you've exhausted all other
|
50 percent or 100 percent improvement when you've exhausted all other
|
||||||
avenues.
|
avenues.
|
||||||
|
|
||||||
------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
> 
|
||||||
 *The point is simply this: You can gain far more by stepping back a bit and thinking of the fastest overall way for the CPU to perform a task than you can by saving a cycle here or there using different instructions. Try to think at the level of sequences of instructions rather than individual instructions, and learn to treat x86 instructions as building blocks with unique characteristics rather than as instructions dedicated to specific tasks.*
|
> The point is simply this: You can gain far more by stepping back a bit
|
||||||
------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
> and thinking of the fastest overall way for the CPU to perform a task
|
||||||
|
> than you can by saving a cycle here or there using different
|
||||||
|
> instructions. Try to think at the level of sequences of instructions
|
||||||
|
> rather than individual instructions, and learn to treat x86 instructions
|
||||||
|
> as building blocks with unique characteristics rather than as
|
||||||
|
> instructions dedicated to specific tasks.
|
||||||
|
|
|
||||||
26
07-05.md
26
07-05.md
|
|
@ -64,9 +64,13 @@ code snippet in Listing 7.4.
|
||||||
BIT_PATTERN=BIT_PATTERN SHL 1
|
BIT_PATTERN=BIT_PATTERN SHL 1
|
||||||
ENDM
|
ENDM
|
||||||
|
|
||||||
------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
> 
|
||||||
 *Besides illustrating the advantages of local optimization, this example also shows that it generally pays to precalculate results; this is often done at or before assembly time, but precalculated tables can also be built at run time. This is merely one aspect of a fundamental optimization rule: Move as much work as possible out of your critical code by whatever means necessary.*
|
> Besides illustrating the advantages of local optimization, this example
|
||||||
------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
> also shows that it generally pays to precalculate results; this is often
|
||||||
|
> done at or before assembly time, but precalculated tables can also be
|
||||||
|
> built at run time. This is merely one aspect of a fundamental
|
||||||
|
> optimization rule: Move as much work as possible out of your critical
|
||||||
|
> code by whatever means necessary.
|
||||||
|
|
||||||
#### NOT Flips Bits—Not Flags {#Heading9}
|
#### NOT Flips Bits—Not Flags {#Heading9}
|
||||||
|
|
||||||
|
|
@ -84,9 +88,11 @@ 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
|
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.
|
it can be handy to keep the flags unmodified for later testing.
|
||||||
|
|
||||||
------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
> 
|
||||||
 *Besides, if you want to **NOT** an operand and set the flags in the process, you can just **XOR** it with -1. Put another way, the only functional difference between **NOT AX** and **XOR AX,0FFFFH** is that **XOR** modifies the flags and **NOT** doesn't.*
|
> Besides, if you want to **NOT** an operand and set the flags in the
|
||||||
------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
> process, you can just **XOR** it with -1. Put another way, the only
|
||||||
|
> functional difference between **NOT AX** and **XOR AX,0FFFFH** is that
|
||||||
|
> **XOR** modifies the flags and **NOT** doesn't.
|
||||||
|
|
||||||
The x86 instruction set offers many ways to accomplish almost any task.
|
The x86 instruction set offers many ways to accomplish almost any task.
|
||||||
Understanding the subtle distinctions between the instructions—whether
|
Understanding the subtle distinctions between the instructions—whether
|
||||||
|
|
@ -152,9 +158,11 @@ altering the arithmetic flags is a common characteristic of program
|
||||||
control instructions (as opposed to arithmetic and logical instructions
|
control instructions (as opposed to arithmetic and logical instructions
|
||||||
like **SUB** and **AND,** which do alter the flags).
|
like **SUB** and **AND,** which do alter the flags).
|
||||||
|
|
||||||
------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
> 
|
||||||
 *The rule is not that the arithmetic flags change whenever the CPU performs a calculation; rather, the flags change whenever you execute an arithmetic, logical, or flag control (such as **CLC** to clear the Carry flag) instruction.*
|
> The rule is not that the arithmetic flags change whenever the CPU
|
||||||
------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
> performs a calculation; rather, the flags change whenever you execute an
|
||||||
|
> arithmetic, logical, or flag control (such as **CLC** to clear the Carry
|
||||||
|
> flag) instruction.
|
||||||
|
|
||||||
Not only do **LOOP** and **JCXZ** not alter the flags, but **REP MOVS**,
|
Not only do **LOOP** and **JCXZ** not alter the flags, but **REP MOVS**,
|
||||||
which counts down CX to 0, doesn't affect the flags either.
|
which counts down CX to 0, doesn't affect the flags either.
|
||||||
|
|
|
||||||
19
08-01.md
19
08-01.md
|
|
@ -50,9 +50,12 @@ C/C++ and Watcom C are by now pretty good at fine-tuning C code, and
|
||||||
you're not likely to do much better by taking the compiler's assembly
|
you're not likely to do much better by taking the compiler's assembly
|
||||||
language output and tweaking it.
|
language output and tweaking it.
|
||||||
|
|
||||||
------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
> 
|
||||||
 *To make the process of translating C code to assembly language worth the trouble, you must ignore what the compiler does and design your assembly language code from a pure assembly language perspective. With a merely adequate translation, you risk laboring mightily for little or no reward.*
|
> To make the process of translating C code to assembly language worth the
|
||||||
------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
> trouble, you must ignore what the compiler does and design your assembly
|
||||||
|
> language code from a pure assembly language perspective. With a merely
|
||||||
|
> adequate translation, you risk laboring mightily for little or no
|
||||||
|
> reward.
|
||||||
|
|
||||||
Apropos of which, when was the last time you heard of Terry Jacks?
|
Apropos of which, when was the last time you heard of Terry Jacks?
|
||||||
|
|
||||||
|
|
@ -88,9 +91,13 @@ Also make it a point to concentrate on refining your program design and
|
||||||
algorithmic approach at the conceptual and/or C levels before doing any
|
algorithmic approach at the conceptual and/or C levels before doing any
|
||||||
assembly language optimization.
|
assembly language optimization.
|
||||||
|
|
||||||
------------------- --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
> 
|
||||||
 *Assembly language optimization is the final and far from the only step in the optimization chain, and as such should be performed last; converting to assembly too soon can lock in your code before the design is optimal. At the very least, conversion to assembly tends to make future changes and debugging more difficult, slowing you down and limiting your options.*
|
> Assembly language optimization is the final and far from the only step
|
||||||
------------------- --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
> in the optimization chain, and as such should be performed last;
|
||||||
|
> converting to assembly too soon can lock in your code before the design
|
||||||
|
> is optimal. At the very least, conversion to assembly tends to make
|
||||||
|
> future changes and debugging more difficult, slowing you down and
|
||||||
|
> limiting your options.
|
||||||
|
|
||||||
### Don't Call Your Functions on Me, Baby {#Heading4}
|
### Don't Call Your Functions on Me, Baby {#Heading4}
|
||||||
|
|
||||||
|
|
|
||||||
25
08-02.md
25
08-02.md
|
|
@ -37,9 +37,11 @@ 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
|
confuse every compiler I've seen, causing the full segment:offset
|
||||||
address to be reloaded each time either pointer is used.
|
address to be reloaded each time either pointer is used.
|
||||||
|
|
||||||
------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
> 
|
||||||
 *This particularly affects performance in 286 protected mode (under OS/2 1.X or the Rational DOS Extender, for example) because segment loads in protected mode take a minimum of 17 cycles, versus a mere 2 cycles in real mode.*
|
> This particularly affects performance in 286 protected mode (under OS/2
|
||||||
------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
> 1.X or the Rational DOS Extender, for example) because segment loads in
|
||||||
|
> protected mode take a minimum of 17 cycles, versus a mere 2 cycles in
|
||||||
|
> real mode.
|
||||||
|
|
||||||
In assembly language you have full control over segments. Use it, and,
|
In assembly language you have full control over segments. Use it, and,
|
||||||
if necessary, reorganize your code to minimize segment loading.
|
if necessary, reorganize your code to minimize segment loading.
|
||||||
|
|
@ -61,9 +63,11 @@ Am I saying that C compilers produce better code than you do? No, I'm
|
||||||
saying that they *can,* unless you use assembly language properly.
|
saying that they *can,* unless you use assembly language properly.
|
||||||
Writing code in assembly language rather than C guarantees nothing.
|
Writing code in assembly language rather than C guarantees nothing.
|
||||||
|
|
||||||
------------------- ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
> 
|
||||||
 *You can write good assembly, bad assembly, or assembly that is virtually indistinguishable from compiled code; you are more likely than not to write the latter if you think that optimization consists of tweaking compiled C code.*
|
> You can write good assembly, bad assembly, or assembly that is virtually
|
||||||
------------------- ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
> indistinguishable from compiled code; you are more likely than not to
|
||||||
|
> write the latter if you think that optimization consists of tweaking
|
||||||
|
> compiled C code.
|
||||||
|
|
||||||
Sure, you can probably use the registers more efficiently and take
|
Sure, you can probably use the registers more efficiently and take
|
||||||
advantage of an instruction or two that the compiler missed, but the
|
advantage of an instruction or two that the compiler missed, but the
|
||||||
|
|
@ -109,9 +113,12 @@ enough, there's no reason why you can't reorganize the data. This might
|
||||||
mean removing the array elements from the structures and storing them in
|
mean removing the array elements from the structures and storing them in
|
||||||
their own array so that **REP SCASW** *could* be used.
|
their own array so that **REP SCASW** *could* be used.
|
||||||
|
|
||||||
------------------- ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
> 
|
||||||
 *Organizing a program's data so that the performance of the critical sections can be optimized is a key part of design, and one that's easily shortchanged unless, during the design stage, you thoroughly understand and work to bring together your data needs, the critical sections of your program, and potential assembly language optimizations.*
|
> Organizing a program's data so that the performance of the critical
|
||||||
------------------- ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
> sections can be optimized is a key part of design, and one that's easily
|
||||||
|
> shortchanged unless, during the design stage, you thoroughly understand
|
||||||
|
> and work to bring together your data needs, the critical sections of
|
||||||
|
> your program, and potential assembly language optimizations.
|
||||||
|
|
||||||
More on this shortly.
|
More on this shortly.
|
||||||
|
|
||||||
|
|
|
||||||
25
09-02.md
25
09-02.md
|
|
@ -68,9 +68,13 @@ now on) by arranging your operands so that the multiplier (always the
|
||||||
rightmost operand following **MUL** or **IMUL**) is no larger than the
|
rightmost operand following **MUL** or **IMUL**) is no larger than the
|
||||||
other operand.
|
other operand.
|
||||||
|
|
||||||
------------------- ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
> 
|
||||||
 *Why? Because the 386 processes one multiplier bit per cycle and immediately ends a multiplication when all significant bits of the multiplier have been processed, so fewer cycles are required to multiply a large multiplicand times a small multiplier than a small multiplicand times a large multiplier, by a factor of about 1 cycle for each significant multiplier bit eliminated.*
|
> Why? Because the 386 processes one multiplier bit per cycle and
|
||||||
------------------- ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
> immediately ends a multiplication when all significant bits of the
|
||||||
|
> multiplier have been processed, so fewer cycles are required to multiply
|
||||||
|
> a large multiplicand times a small multiplier than a small multiplicand
|
||||||
|
> times a large multiplier, by a factor of about 1 cycle for each
|
||||||
|
> significant multiplier bit eliminated.
|
||||||
|
|
||||||
(There's a minimum execution time on this trick; below 3 significant
|
(There's a minimum execution time on this trick; below 3 significant
|
||||||
multiplier bits, no additional cycles are saved.) For example,
|
multiplier bits, no additional cycles are saved.) For example,
|
||||||
|
|
@ -89,9 +93,11 @@ This highlights another interesting point: **MUL** and **IMUL** on the
|
||||||
386 are so fast that alternative multiplication approaches, while
|
386 are so fast that alternative multiplication approaches, while
|
||||||
generally still faster, are worthwhile only in truly time-critical code.
|
generally still faster, are worthwhile only in truly time-critical code.
|
||||||
|
|
||||||
------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
> 
|
||||||
 *On 386SXs and uncached 386s, where code size can significantly affect performance due to instruction prefetching, the compact **MUL** and **IMUL** instructions can approach and in some cases even outperform the "optimized" alternatives.*
|
> On 386SXs and uncached 386s, where code size can significantly affect
|
||||||
------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
> performance due to instruction prefetching, the compact **MUL** and
|
||||||
|
> **IMUL** instructions can approach and in some cases even outperform the
|
||||||
|
> "optimized" alternatives.
|
||||||
|
|
||||||
All in all, **MUL** and **IMUL** are reasonable performers on the 386,
|
All in all, **MUL** and **IMUL** are reasonable performers on the 386,
|
||||||
no longer to be avoided in most cases—and you can help that along by
|
no longer to be avoided in most cases—and you can help that along by
|
||||||
|
|
@ -127,9 +133,10 @@ deal is handling **REPNZ SCASB** matches, which require checking the
|
||||||
remainder of the string with **REPZ CMPS** and restarting **REPNZ
|
remainder of the string with **REPZ CMPS** and restarting **REPNZ
|
||||||
SCASB** if no match is found.
|
SCASB** if no match is found.
|
||||||
|
|
||||||
------------------- ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
> 
|
||||||
 *Rob points out that the number of **REPNZ SCASB** matches can easily be reduced simply by scanning for the character in the searched-for string that appears least often in the buffer being searched.*
|
> Rob points out that the number of **REPNZ SCASB** matches can easily be
|
||||||
------------------- ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
> reduced simply by scanning for the character in the searched-for string
|
||||||
|
> that appears least often in the buffer being searched.
|
||||||
|
|
||||||
Imagine, if you will, that you're searching for the string "EQUAL." By
|
Imagine, if you will, that you're searching for the string "EQUAL." By
|
||||||
my approach, you'd use **REPNZ SCASB** to scan for each occurrence of
|
my approach, you'd use **REPNZ SCASB** to scan for each occurrence of
|
||||||
|
|
|
||||||
7
09-03.md
7
09-03.md
|
|
@ -31,9 +31,10 @@ counting. Listings 9.1 and 9.2 use pretty much the same instructions,
|
||||||
and even use the same approach of scanning with **REPNZ SCASB** and
|
and even use the same approach of scanning with **REPNZ SCASB** and
|
||||||
using **REPZ CMPS** to check scanning matches.
|
using **REPZ CMPS** to check scanning matches.
|
||||||
|
|
||||||
------------------- ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
> 
|
||||||
 *The difference between Listings 9.1 and 9.2 (which gives you more than a doubling of performance) is due entirely to understanding the nature of the data being handled, and biasing the code to reflect that knowledge.*
|
> The difference between Listings 9.1 and 9.2 (which gives you more than a
|
||||||
------------------- ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
> doubling of performance) is due entirely to understanding the nature of
|
||||||
|
> the data being handled, and biasing the code to reflect that knowledge.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
|
|
||||||
6
09-06.md
6
09-06.md
|
|
@ -127,9 +127,9 @@ slows performance because instructions must be fetched through the
|
||||||
half-pint 16-bit bus; on a 386, the effect depends on the instruction
|
half-pint 16-bit bus; on a 386, the effect depends on the instruction
|
||||||
mix and whether there's a cache.
|
mix and whether there's a cache.
|
||||||
|
|
||||||
------------------- ----------------------------------------------------------------------------------------------------------------------------------------------
|
> 
|
||||||
 *On balance, though, it's as important to keep your most-used variables in the stack's sweet spot in 386 native mode as it was on the 8088.*
|
> On balance, though, it's as important to keep your most-used variables
|
||||||
------------------- ----------------------------------------------------------------------------------------------------------------------------------------------
|
> in the stack's sweet spot in 386 native mode as it was on the 8088.
|
||||||
|
|
||||||
In assembly, it's easy to control the organization of your stack frame.
|
In assembly, it's easy to control the organization of your stack frame.
|
||||||
In C, however, you'll have to figure out the allocation scheme your
|
In C, however, you'll have to figure out the allocation scheme your
|
||||||
|
|
|
||||||
9
10-01.md
9
10-01.md
|
|
@ -67,9 +67,12 @@ always hungered to get results from my work as soon as possible; I
|
||||||
gravitated toward graphics for its instant and very visible
|
gravitated toward graphics for its instant and very visible
|
||||||
gratification. Over time, however, I've learned patience.
|
gratification. Over time, however, I've learned patience.
|
||||||
|
|
||||||
------------------- ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
> 
|
||||||
 *I've come to spend an increasingly large portion of my time choosing algorithms, designing, and simply giving my mind quiet time in which to work on problems and come up with non-obvious approaches before coding; and I've found that the extra time up front more than pays for itself in both decreased coding time and superior programs.*
|
> I've come to spend an increasingly large portion of my time choosing
|
||||||
------------------- ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
> algorithms, designing, and simply giving my mind quiet time in which to
|
||||||
|
> work on problems and come up with non-obvious approaches before coding;
|
||||||
|
> and I've found that the extra time up front more than pays for itself in
|
||||||
|
> both decreased coding time and superior programs.
|
||||||
|
|
||||||
In this chapter, I'm going to walk you through a simple but illustrative
|
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
|
case history that nicely points up the wisdom of delaying gratification
|
||||||
|
|
|
||||||
10
10-03.md
10
10-03.md
|
|
@ -93,9 +93,13 @@ 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
|
especially for large numbers (and imagine if we were working with large
|
||||||
*longs!*).
|
*longs!*).
|
||||||
|
|
||||||
------------------- --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
> 
|
||||||
 *Had I been implementing GCD determination without Sedgewick's help, I would surely not have settled for Listing 10.1—but I might well have ended up with Listing 10.2 in my enthusiasm over the "brilliant" discovery of subtracting the lesser Using Euclid's algorithm to find a GCD number from the greater. In a commercial product, my lack of patience and discipline could have been costly indeed.*
|
> Had I been implementing GCD determination without Sedgewick's help, I
|
||||||
------------------- --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
> would surely not have settled for Listing 10.1—but I might well have
|
||||||
|
> ended up with Listing 10.2 in my enthusiasm over the "brilliant"
|
||||||
|
> discovery of subtracting the lesser Using Euclid's algorithm to find a
|
||||||
|
> GCD number from the greater. In a commercial product, my lack of
|
||||||
|
> patience and discipline could have been costly indeed.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
|
|
||||||
8
10-04.md
8
10-04.md
|
|
@ -106,9 +106,11 @@ lousy at; compilers could out-optimize you at this level with one pass
|
||||||
tied behind their back *if* they knew as much about the code you're
|
tied behind their back *if* they knew as much about the code you're
|
||||||
writing as you do, which they don't.
|
writing as you do, which they don't.
|
||||||
|
|
||||||
------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
> 
|
||||||
 *Design optimization—conceptual breakthroughs in understanding the relationships between the needs of an application, the nature of the data the application works with, and what the computer can do—is global pattern matching.*
|
> Design optimization—conceptual breakthroughs in understanding the
|
||||||
------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
> relationships between the needs of an application, the nature of the
|
||||||
|
> data the application works with, and what the computer can do—is global
|
||||||
|
> pattern matching.
|
||||||
|
|
||||||
Computers are *much* worse at that sort of pattern matching than humans;
|
Computers are *much* worse at that sort of pattern matching than humans;
|
||||||
computers have no way to integrate vast amounts of disparate
|
computers have no way to integrate vast amounts of disparate
|
||||||
|
|
|
||||||
8
11-02.md
8
11-02.md
|
|
@ -73,9 +73,11 @@ exact number of wait states inserted at any given time depends on the
|
||||||
interaction between the code being executed and the memory system it's
|
interaction between the code being executed and the memory system it's
|
||||||
running on.
|
running on.
|
||||||
|
|
||||||
------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
> 
|
||||||
 *The performance of most 386 memory systems can vary greatly from one memory access to another, depending on factors such as what data happens to be in the cache and which interleaved bank and/or RAM column was accessed last.*
|
> The performance of most 386 memory systems can vary greatly from one
|
||||||
------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
> memory access to another, depending on factors such as what data happens
|
||||||
|
> to be in the cache and which interleaved bank and/or RAM column was
|
||||||
|
> accessed last.
|
||||||
|
|
||||||
The many memory systems in use make it impossible for us to optimize for
|
The many memory systems in use make it impossible for us to optimize for
|
||||||
286/386 computers with the precision that's possible on the 8088.
|
286/386 computers with the precision that's possible on the 8088.
|
||||||
|
|
|
||||||
6
11-03.md
6
11-03.md
|
|
@ -69,9 +69,9 @@ The penalty for performing a word-sized access starting at an odd
|
||||||
address is easy to calculate: Two accesses take twice as long as one
|
address is easy to calculate: Two accesses take twice as long as one
|
||||||
access.
|
access.
|
||||||
|
|
||||||
------------------- ------------------------------------------------------------------------------------------------------------------------------------------------
|
> 
|
||||||
 *In other words, the effective capacity of the 286's external data bus is* *halved* *when a word-sized access to an odd address is performed.*
|
> In other words, the effective capacity of the 286's external data bus
|
||||||
------------------- ------------------------------------------------------------------------------------------------------------------------------------------------
|
> is *halved* when a word-sized access to an odd address is performed.
|
||||||
|
|
||||||
That, in a nutshell, is the data alignment cycle-eater, the one new
|
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
|
cycle-eater of the 286 and 386. (The data alignment cycle-eater is a
|
||||||
|
|
|
||||||
11
11-04.md
11
11-04.md
|
|
@ -53,9 +53,9 @@ instruction if necessary, and the time required to execute a **NOP** can
|
||||||
sometimes cancel the performance advantage of having a word-aligned
|
sometimes cancel the performance advantage of having a word-aligned
|
||||||
branch destination.
|
branch destination.
|
||||||
|
|
||||||
------------------- -----------------------------------------------------------------------------------------------------------------
|
> 
|
||||||
 *Consequently, it's best to word-align only those branch destinations that can be reached solely by branching.*
|
> Consequently, it's best to word-align only those branch destinations
|
||||||
------------------- -----------------------------------------------------------------------------------------------------------------
|
> that can be reached solely by branching.
|
||||||
|
|
||||||
I recommend that you only go out of your way to word-align the start
|
I recommend that you only go out of your way to word-align the start
|
||||||
offsets of your subroutines, as in:
|
offsets of your subroutines, as in:
|
||||||
|
|
@ -112,9 +112,8 @@ interrupt occurring between the two instructions will be serviced more
|
||||||
slowly than it normally would. The same goes for decrementing twice; use
|
slowly than it normally would. The same goes for decrementing twice; use
|
||||||
**SUB SP,2** instead.
|
**SUB SP,2** instead.
|
||||||
|
|
||||||
------------------- ------------------------------------------------
|
> 
|
||||||
 *Keep the stack pointer aligned at all times.*
|
> Keep the stack pointer aligned at all times.
|
||||||
------------------- ------------------------------------------------
|
|
||||||
|
|
||||||
#### The DRAM Refresh Cycle-Eater: Still an Act of God {#Heading11 align="center"}
|
#### The DRAM Refresh Cycle-Eater: Still an Act of God {#Heading11 align="center"}
|
||||||
|
|
||||||
|
|
|
||||||
9
11-05.md
9
11-05.md
|
|
@ -61,9 +61,12 @@ adapter cycle-eater, as measured in cycles lost to wait states, is
|
||||||
indeed much worse on AT-class computers than it is on the PC, and it's
|
indeed much worse on AT-class computers than it is on the PC, and it's
|
||||||
worse still on more powerful computers.
|
worse still on more powerful computers.
|
||||||
|
|
||||||
------------------- ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
> 
|
||||||
 *How bad is the display adapter cycle-eater on an AT? It's this bad: Based on my (not inconsiderable) experience in timing display adapter access, I've found that the display adapter cycle-eater can slow an AT—or even a 386 computer—to near-PC speeds when display memory is accessed.*
|
> How bad is the display adapter cycle-eater on an AT? It's this bad:
|
||||||
------------------- ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
> Based on my (not inconsiderable) experience in timing display adapter
|
||||||
|
> access, I've found that the display adapter cycle-eater can slow an
|
||||||
|
> AT—or even a 386 computer—to near-PC speeds when display memory is
|
||||||
|
> accessed.
|
||||||
|
|
||||||
I know that's hard to believe, but the display adapter cycle-eater gives
|
I know that's hard to believe, but the display adapter cycle-eater gives
|
||||||
out just so many display memory accesses in a given time, and no more,
|
out just so many display memory accesses in a given time, and no more,
|
||||||
|
|
|
||||||
7
11-06.md
7
11-06.md
|
|
@ -21,9 +21,10 @@ to address a full 4 gigabytes per segment, but in real mode you're still
|
||||||
limited to 64K, even with 32-bit registers and the new addressing modes,
|
limited to 64K, even with 32-bit registers and the new addressing modes,
|
||||||
unless you play some unorthodox tricks with the segment registers.
|
unless you play some unorthodox tricks with the segment registers.
|
||||||
|
|
||||||
------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
> 
|
||||||
 *Note well: Those tricks don't necessarily work with system software such as Windows, so I'd recommend against using them. If you want 4-gigabyte segments, use a 32-bit environment such as Win32.*
|
> Note well: Those tricks don't necessarily work with system software such
|
||||||
------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
> as Windows, so I'd recommend against using them. If you want 4-gigabyte
|
||||||
|
> segments, use a 32-bit environment such as Win32.
|
||||||
|
|
||||||
#### Optimization Rules: The More Things Change... {#Heading15 align="center"}
|
#### Optimization Rules: The More Things Change... {#Heading15 align="center"}
|
||||||
|
|
||||||
|
|
|
||||||
19
12-01.md
19
12-01.md
|
|
@ -84,9 +84,14 @@ for earlier x86 processors. Generally, I'll be discussing optimization
|
||||||
for real mode (it being the most widely used mode at the moment),
|
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.
|
although many of the rules should apply to protected mode as well.
|
||||||
|
|
||||||
------------------- --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
> 
|
||||||
 *486 optimization is generally more precise and less frustrating than optimization for other x86 processors because every 486 has an identical internal cache. Whenever both the instructions being executed and the data the instructions access are in the cache, those instructions will run in a consistent and calculatable number of cycles on all 486s, with little chance of interference from the prefetch queue and without regard to the speed of external memory.*
|
> 486 optimization is generally more precise and less frustrating than
|
||||||
------------------- --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
> optimization for other x86 processors because every 486 has an identical
|
||||||
|
> internal cache. Whenever both the instructions being executed and the
|
||||||
|
> data the instructions access are in the cache, those instructions will
|
||||||
|
> run in a consistent and calculatable number of cycles on all 486s, with
|
||||||
|
> little chance of interference from the prefetch queue and without regard
|
||||||
|
> to the speed of external memory.
|
||||||
|
|
||||||
In other words, for cached code (which time-critical code almost always
|
In other words, for cached code (which time-critical code almost always
|
||||||
is), performance is predictable and can be calculated with good
|
is), performance is predictable and can be calculated with good
|
||||||
|
|
@ -115,9 +120,11 @@ matter what register is used.) **MOV AX, [BX+DI]** and **MOV CL,
|
||||||
[BP+SI+10]** perform indexed addressing; **MOV AX,[BX]** and **MOV DL,
|
[BP+SI+10]** perform indexed addressing; **MOV AX,[BX]** and **MOV DL,
|
||||||
[SI+1]** do not.
|
[SI+1]** do not.
|
||||||
|
|
||||||
------------------- --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
> 
|
||||||
 *Therefore, in real mode, the rule is to avoid using two registers to point to memory whenever possible. Often, this simply means adding the two registers together outside a loop before memory is actually addressed.*
|
> Therefore, in real mode, the rule is to avoid using two registers to
|
||||||
------------------- --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
> point to memory whenever possible. Often, this simply means adding the
|
||||||
|
> two registers together outside a loop before memory is actually
|
||||||
|
> addressed.
|
||||||
|
|
||||||
As an example, you might adhere to this rule by replacing the code
|
As an example, you might adhere to this rule by replacing the code
|
||||||
|
|
||||||
|
|
|
||||||
7
12-02.md
7
12-02.md
|
|
@ -88,9 +88,10 @@ documentation understates the extent of the penalty for interrupting the
|
||||||
address calculation pipeline by loading a memory pointer just before
|
address calculation pipeline by loading a memory pointer just before
|
||||||
it's used.
|
it's used.
|
||||||
|
|
||||||
------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
> 
|
||||||
 *The truth of the matter appears to be that if a register is the destination of one instruction and is then used by the next instruction to address memory in real mode, not one but two cycles are lost!*
|
> The truth of the matter appears to be that if a register is the
|
||||||
------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
> destination of one instruction and is then used by the next instruction
|
||||||
|
> to address memory in real mode, not one but two cycles are lost!
|
||||||
|
|
||||||
In 32-bit protected mode, however, the penalty is, in fact, the 1 cycle
|
In 32-bit protected mode, however, the penalty is, in fact, the 1 cycle
|
||||||
that Intel .
|
that Intel .
|
||||||
|
|
|
||||||
7
12-03.md
7
12-03.md
|
|
@ -146,9 +146,10 @@ mentioned in this chapter apply to **XLAT**, apparently because **XLAT**
|
||||||
is so slow—4 cycles—that it gives the 486 time to perform addressing
|
is so slow—4 cycles—that it gives the 486 time to perform addressing
|
||||||
calculations during the course of the instruction.
|
calculations during the course of the instruction.
|
||||||
|
|
||||||
------------------- ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
> 
|
||||||
 *While it's nice that **XLAT** doesn't suffer from the various 486 addressing penalties, the reason for that is basically that **XLAT** is slow, so there's still no compelling reason to use **XLAT** on the 486.*
|
> While it's nice that **XLAT** doesn't suffer from the various 486
|
||||||
------------------- ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
> addressing penalties, the reason for that is basically that **XLAT** is
|
||||||
|
> slow, so there's still no compelling reason to use **XLAT** on the 486.
|
||||||
|
|
||||||
In general, penalties for interrupting the 486's pipeline apply
|
In general, penalties for interrupting the 486's pipeline apply
|
||||||
primarily to the fast core instructions of the 486, most notably
|
primarily to the fast core instructions of the 486, most notably
|
||||||
|
|
|
||||||
14
12-04.md
14
12-04.md
|
|
@ -75,9 +75,11 @@ presumably, **MOV CL,AL** interrupts the pipeline again because the
|
||||||
pipeline is now on its first cycle: the one that loading a byte register
|
pipeline is now on its first cycle: the one that loading a byte register
|
||||||
can affect.
|
can affect.
|
||||||
|
|
||||||
------------------- -----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
> 
|
||||||
 *I know—it seems awfully complicated. It isn't, really. Generally, try not to use byte destinations exactly two cycles before using a register to address memory, and try not to load a register either one or two cycles before using it to address memory, and you'll be fine.*
|
> I know—it seems awfully complicated. It isn't, really. Generally, try
|
||||||
------------------- -----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
> not to use byte destinations exactly two cycles before using a register
|
||||||
|
> to address memory, and try not to load a register either one or two
|
||||||
|
> cycles before using it to address memory, and you'll be fine.
|
||||||
|
|
||||||
#### Timing Your Own 486 Code {#Heading11}
|
#### Timing Your Own 486 Code {#Heading11}
|
||||||
|
|
||||||
|
|
@ -143,9 +145,9 @@ 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
|
seven-tenths of a cycle comes from fetching non-cached instruction
|
||||||
bytes.
|
bytes.
|
||||||
|
|
||||||
------------------- -----------------------------------------------------------------------------------------------------------------------
|
> 
|
||||||
 *Whenever you see non-integral timing results of this sort, it's a good bet that the test code or data isn't cached.*
|
> Whenever you see non-integral timing results of this sort, it's a good
|
||||||
------------------- -----------------------------------------------------------------------------------------------------------------------
|
> bet that the test code or data isn't cached.
|
||||||
|
|
||||||
### The Story Continues {#Heading12}
|
### The Story Continues {#Heading12}
|
||||||
|
|
||||||
|
|
|
||||||
7
13-01.md
7
13-01.md
|
|
@ -106,9 +106,10 @@ the intervening instruction takes two cycles, there's no penalty at all.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
------------------- ----------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
> 
|
||||||
 *Remember, pipeline penalties diminish with increasing number of cycles, not instructions, between the pipeline disrupter and the potentially affected instruction.*
|
> Remember, pipeline penalties diminish with increasing number of cycles,
|
||||||
------------------- ----------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
> not instructions, between the pipeline disrupter and the potentially
|
||||||
|
> affected instruction.
|
||||||
|
|
||||||
**LISTING 13.2 L13-2.ASM**
|
**LISTING 13.2 L13-2.ASM**
|
||||||
|
|
||||||
|
|
|
||||||
10
13-03.md
10
13-03.md
|
|
@ -68,9 +68,13 @@ operations include all the string instructions except **REP MOVS,** as
|
||||||
well as **XLAT, LOOP,** and, of course, **PUSH *mem*** and **POP
|
well as **XLAT, LOOP,** and, of course, **PUSH *mem*** and **POP
|
||||||
*mem.***
|
*mem.***
|
||||||
|
|
||||||
------------------- ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
> 
|
||||||
 *Whenever possible, try to use the 486's 1-cycle instructions, including **MOV, ADD, SUB, CMP, ADC, SBB, XOR, AND, OR, TEST, LEA**, and **PUSH reg** and **POP reg**. These instructions have an added benefit in that it's often possible to rearrange them for maximum pipeline efficiency, as is the case with Terje's optimization described earlier in this chapter.*
|
> Whenever possible, try to use the 486's 1-cycle instructions, including
|
||||||
------------------- ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
> **MOV, ADD, SUB, CMP, ADC, SBB, XOR, AND, OR, TEST, LEA**, and **PUSH
|
||||||
|
> reg** and **POP reg**. These instructions have an added benefit in that
|
||||||
|
> it's often possible to rearrange them for maximum pipeline efficiency,
|
||||||
|
> as is the case with Terje's optimization described earlier in this
|
||||||
|
> chapter.
|
||||||
|
|
||||||
### Optimal 1-Bit Shifts and Rotates {#Heading6}
|
### Optimal 1-Bit Shifts and Rotates {#Heading6}
|
||||||
|
|
||||||
|
|
|
||||||
9
13-04.md
9
13-04.md
|
|
@ -63,9 +63,12 @@ example, **MOV AL, [EBX]** is a 2-byte instruction; **MOV AL,
|
||||||
[EBX+10H]** is a 3-byte instruction; and **MOV AL, [EBX+10000H]** is a
|
[EBX+10H]** is a 3-byte instruction; and **MOV AL, [EBX+10000H]** is a
|
||||||
6-byte instruction.
|
6-byte instruction.
|
||||||
|
|
||||||
------------------- ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
> 
|
||||||
 *Note that 1 and 4-byte displacements, but not 2-byte displacements, are supported for 32-bit addressing. Code size can be greatly improved by keeping stack frame variables within 128 bytes of EBP, and variables in pointed-to structures within 127 bytes of the start of the structure, so that displacements can be 1 rather than 4 bytes.*
|
> Note that 1 and 4-byte displacements, but not 2-byte displacements, are
|
||||||
------------------- ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
> supported for 32-bit addressing. Code size can be greatly improved by
|
||||||
|
> keeping stack frame variables within 128 bytes of EBP, and variables in
|
||||||
|
> pointed-to structures within 127 bytes of the start of the structure, so
|
||||||
|
> that displacements can be 1 rather than 4 bytes.
|
||||||
|
|
||||||
However, because 32-bit addressing supports many more addressing
|
However, because 32-bit addressing supports many more addressing
|
||||||
combinations than 16-bit addressing, the Mod-R/M byte can't describe all
|
combinations than 16-bit addressing, the Mod-R/M byte can't describe all
|
||||||
|
|
|
||||||
7
14-01.md
7
14-01.md
|
|
@ -100,9 +100,10 @@ It's important to understand that we're assuming that the buffer is
|
||||||
typical text. That's what I meant at the outset, when I said that the
|
typical text. That's what I meant at the outset, when I said that the
|
||||||
information you need may be under your nose.
|
information you need may be under your nose.
|
||||||
|
|
||||||
------------------- --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
> 
|
||||||
 *Formally, you don't know a blessed thing about the search buffer, but experience, common sense, and your knowledge of the application give you a great deal of useful, if somewhat imprecise, information.*
|
> Formally, you don't know a blessed thing about the search buffer, but
|
||||||
------------------- --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
> experience, common sense, and your knowledge of the application give you
|
||||||
|
> a great deal of useful, if somewhat imprecise, information.
|
||||||
|
|
||||||
If the buffer contains the letter ‘A' repeated 1,000 times, followed by
|
If the buffer contains the letter ‘A' repeated 1,000 times, followed by
|
||||||
the letter ‘B,' then the **REPNZ SCASB/REPZ CMPS** approach will be much
|
the letter ‘B,' then the **REPNZ SCASB/REPZ CMPS** approach will be much
|
||||||
|
|
|
||||||
7
14-02.md
7
14-02.md
|
|
@ -54,9 +54,10 @@ of bytes (a portion of the buffer); it doesn't matter in the least in
|
||||||
what order we compare them, so long as all the bytes in one set are
|
what order we compare them, so long as all the bytes in one set are
|
||||||
compared to the corresponding bytes in the other set.
|
compared to the corresponding bytes in the other set.
|
||||||
|
|
||||||
------------------- -----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
> 
|
||||||
 *Why on earth would we want to start with the rightmost character? Because a mismatch on the rightmost character tells us a great deal more than a mismatch on the leftmost character.*
|
> Why on earth would we want to start with the rightmost character?
|
||||||
------------------- -----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
> Because a mismatch on the rightmost character tells us a great deal more
|
||||||
|
> than a mismatch on the leftmost character.
|
||||||
|
|
||||||
We learn nothing new from a mismatch on the leftmost character, except
|
We learn nothing new from a mismatch on the leftmost character, except
|
||||||
that the pattern can't match starting at that location. A mismatch on
|
that the pattern can't match starting at that location. A mismatch on
|
||||||
|
|
|
||||||
8
15-02.md
8
15-02.md
|
|
@ -96,9 +96,11 @@ have learned it by spending five minutes with Sedgewick's book.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
> 
|
||||||
 *The next-node pointer of the head node, which points to the first real node, is the only part of the head node that's actually used. This way the same code works on the head node as on the rest of the list, so there are no special cases.*
|
> The next-node pointer of the head node, which points to the first real
|
||||||
------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
> node, is the only part of the head node that's actually used. This way
|
||||||
|
> the same code works on the head node as on the rest of the list, so
|
||||||
|
> there are no special cases.
|
||||||
|
|
||||||
Likewise, there should be a separate node for the tail of the list, so
|
Likewise, there should be a separate node for the tail of the list, so
|
||||||
that every node that contains real data is guaranteed to have a node on
|
that every node that contains real data is guaranteed to have a node on
|
||||||
|
|
|
||||||
5
16-03.md
5
16-03.md
|
|
@ -142,9 +142,8 @@ One possible optimization is unrolling the loop, although that is truly
|
||||||
a last resort because it tends to make further changes extremely
|
a last resort because it tends to make further changes extremely
|
||||||
difficult.
|
difficult.
|
||||||
|
|
||||||
------------------- -----------------------------------------------------------
|
> 
|
||||||
 *Exhaust all other optimizations before unrolling loops.*
|
> Exhaust all other optimizations before unrolling loops.
|
||||||
------------------- -----------------------------------------------------------
|
|
||||||
|
|
||||||
### Challenges and Hazards {#Heading5}
|
### Challenges and Hazards {#Heading5}
|
||||||
|
|
||||||
|
|
|
||||||
14
16-04.md
14
16-04.md
|
|
@ -52,9 +52,10 @@ base+index addressing (**[EBX+EAX]**), which saves a cycle on the 386.
|
||||||
Second: Changing the instruction to **CMP [EAX],DH** saved 2 cycles—just
|
Second: Changing the instruction to **CMP [EAX],DH** saved 2 cycles—just
|
||||||
enough, by good fortune, to speed up the whole program by 5 percent.
|
enough, by good fortune, to speed up the whole program by 5 percent.
|
||||||
|
|
||||||
------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
> 
|
||||||
 ***CMP reg,[mem]** takes 6 cycles on the 386, but **CMP [ mem ],reg** takes only 5 cycles; you should always perform**CMP** with the memory operand on the left on the 386.*
|
> **CMP reg,[mem]** takes 6 cycles on the 386, but **CMP [ mem ],reg**
|
||||||
------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
> takes only 5 cycles; you should always perform**CMP** with the memory
|
||||||
|
> operand on the left on the 386.
|
||||||
|
|
||||||
(Granted, **CMP [*mem*],*reg*** is 1 cycle slower than **CMP
|
(Granted, **CMP [*mem*],*reg*** is 1 cycle slower than **CMP
|
||||||
*reg*,[*mem*]** on the 286, and they're both the same on the 8088; in
|
*reg*,[*mem*]** on the 286, and they're both the same on the 8088; in
|
||||||
|
|
@ -99,9 +100,10 @@ faster. Consider the incongruity of Terje's willingness to consider a 5
|
||||||
percent speedup significant in light of his later near-doubling of
|
percent speedup significant in light of his later near-doubling of
|
||||||
performance.
|
performance.
|
||||||
|
|
||||||
------------------- --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
> 
|
||||||
 *Don't get stuck in the rut of instruction-by-instruction optimization. It's useful in key loops, but very often, a change in approach will work far greater wonders than any amount of cycle counting can.*
|
> Don't get stuck in the rut of instruction-by-instruction optimization.
|
||||||
------------------- --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
> It's useful in key loops, but very often, a change in approach will work
|
||||||
|
> far greater wonders than any amount of cycle counting can.
|
||||||
|
|
||||||
By the way, Terje's WC50 program is a full-fledged counting program; it
|
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
|
counts characters, words, and lines, can handle multiple files, and lets
|
||||||
|
|
|
||||||
8
16-06.md
8
16-06.md
|
|
@ -22,9 +22,11 @@ don't even guarantee that David Stafford's entry works perfectly,
|
||||||
although, knowing him, it probably does; the more complex and cryptic
|
although, knowing him, it probably does; the more complex and cryptic
|
||||||
the code, the greater the chance for obscure bugs.)
|
the code, the greater the chance for obscure bugs.)
|
||||||
|
|
||||||
------------------- ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
> 
|
||||||
 *Remember, optimize only when needed, and stop when further optimization will not be noticed. Optimization that's not perceptible to the user is like buying Telly Savalas a comb; it's not going to do any harm, but it's nonetheless a waste of time.*
|
> Remember, optimize only when needed, and stop when further optimization
|
||||||
------------------- ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
> will not be noticed. Optimization that's not perceptible to the user is
|
||||||
|
> like buying Telly Savalas a comb; it's not going to do any harm, but
|
||||||
|
> it's nonetheless a waste of time.
|
||||||
|
|
||||||
#### Optimization Level 1: Good Code {#Heading10}
|
#### Optimization Level 1: Good Code {#Heading10}
|
||||||
|
|
||||||
|
|
|
||||||
18
17-03.md
18
17-03.md
|
|
@ -58,9 +58,13 @@ state of our Game of Life implementation, the only areas worth looking
|
||||||
at for possible optimizations are **cell\_state()** and
|
at for possible optimizations are **cell\_state()** and
|
||||||
**next\_generation().**
|
**next\_generation().**
|
||||||
|
|
||||||
------------------- ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
> 
|
||||||
 *It's worth noting, though, that one reason **draw\_pixel()** doesn't much affect performance is that in Listing 17.1, we're smart enough to redraw pixels only when their states change, rather than during every generation. Detecting and eliminating redundant operations is part of knowing the nature of your data, and is a potent optimization technique that will be extremely useful a little later in this chapter.*
|
> It's worth noting, though, that one reason **draw\_pixel()** doesn't
|
||||||
------------------- ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
> much affect performance is that in Listing 17.1, we're smart enough to
|
||||||
|
> redraw pixels only when their states change, rather than during every
|
||||||
|
> generation. Detecting and eliminating redundant operations is part of
|
||||||
|
> knowing the nature of your data, and is a potent optimization technique
|
||||||
|
> that will be extremely useful a little later in this chapter.
|
||||||
|
|
||||||
### The Hazards and Advantages of Abstraction {#Heading6}
|
### The Hazards and Advantages of Abstraction {#Heading6}
|
||||||
|
|
||||||
|
|
@ -81,9 +85,11 @@ know the cell storage format! This is a wonderful thing, in general; it
|
||||||
saves programming time and bugs, and frees you to work on the
|
saves programming time and bugs, and frees you to work on the
|
||||||
application's needs, rather than implementation details.
|
application's needs, rather than implementation details.
|
||||||
|
|
||||||
------------------- ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
> 
|
||||||
 *However, if you never look beneath the surface of the abstract model at the implementation details, you have no idea of what the true performance cost of various operations* *is, and, without that, you have largely surrendered control over performance.*
|
> However, if you never look beneath the surface of the abstract model at
|
||||||
------------------- ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
> the implementation details, you have no idea of what the true performance
|
||||||
|
> cost of various operations is, and, without that, you have largely
|
||||||
|
> surrendered control over performance.
|
||||||
|
|
||||||
Having said that, let me hasten to add that algorithmic improvements can
|
Having said that, let me hasten to add that algorithmic improvements can
|
||||||
make a big difference even when working at a purely abstract level. For
|
make a big difference even when working at a purely abstract level. For
|
||||||
|
|
|
||||||
7
17-05.md
7
17-05.md
|
|
@ -159,6 +159,7 @@ It's undoubtedly possible to improve the performance of Listing 17.4
|
||||||
further by fine-tuning the code, but no tremendous improvement is
|
further by fine-tuning the code, but no tremendous improvement is
|
||||||
possible that way.
|
possible that way.
|
||||||
|
|
||||||
------------------- --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
> 
|
||||||
 *Once you've reached the point of fine-tuning pointer usage and register variables and the like in C or C++, you've become compiler-dependent; you therefore might as well go to assembly and get the real McCoy.*
|
> Once you've reached the point of fine-tuning pointer usage and register
|
||||||
------------------- --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
> variables and the like in C or C++, you've become compiler-dependent;
|
||||||
|
> you therefore might as well go to assembly and get the real McCoy.
|
||||||
9
17-06.md
9
17-06.md
|
|
@ -65,9 +65,12 @@ possible. Large lookup tables, oddly encoded cellmaps, and lots of
|
||||||
bit-twiddling assembly code spring to mind as possible approaches. Can't
|
bit-twiddling assembly code spring to mind as possible approaches. Can't
|
||||||
you just feel your adrenaline start to pump?
|
you just feel your adrenaline start to pump?
|
||||||
|
|
||||||
------------------- ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
> 
|
||||||
 *Relax. Step back. Try to divine the true nature of the problem. The object is not to count neighbors and check cell states as quickly as possible; that's just one possible implementation. The object is to determine when a cell's state must be changed and to change it appropriately, and that's what we need to do as quickly as possible.*
|
> Relax. Step back. Try to divine the true nature of the problem. The
|
||||||
------------------- ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
> object is not to count neighbors and check cell states as quickly as
|
||||||
|
> possible; that's just one possible implementation. The object is to
|
||||||
|
> determine when a cell's state must be changed and to change it
|
||||||
|
> appropriately, and that's what we need to do as quickly as possible.
|
||||||
|
|
||||||
What difference does that new perspective make? Let's approach it this
|
What difference does that new perspective make? Let's approach it this
|
||||||
way. What does a typical cellmap look like? As it happens, after a few
|
way. What does a typical cellmap look like? As it happens, after a few
|
||||||
|
|
|
||||||
11
17-08.md
11
17-08.md
|
|
@ -41,9 +41,14 @@ well-optimized, and, as I noted, the program must be compiled with the
|
||||||
large model for large cellmaps. Also, of course, the entire program is
|
large model for large cellmaps. Also, of course, the entire program is
|
||||||
still in C++; note well that there's not a whit of assembly here.
|
still in C++; note well that there's not a whit of assembly here.
|
||||||
|
|
||||||
------------------- -----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
> 
|
||||||
 *We've gotten more than a 30-times speedup simply by removing a little of the abstraction that C++ encourages, and by storing and processing the data in a manner appropriate for the typical nature of the data itself. In other words, we've done some linear, left-brained optimization (using pointers and reducing calls) and some non-linear, right-brained optimization (understanding the real problem and listening for the creative whisper of non-obvious solutions).*
|
> We've gotten more than a 30-times speedup simply by removing a little of
|
||||||
------------------- -----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
> the abstraction that C++ encourages, and by storing and processing the
|
||||||
|
> data in a manner appropriate for the typical nature of the data itself.
|
||||||
|
> In other words, we've done some linear, left-brained optimization (using
|
||||||
|
> pointers and reducing calls) and some non-linear, right-brained
|
||||||
|
> optimization (understanding the real problem and listening for the
|
||||||
|
> creative whisper of non-obvious solutions).
|
||||||
|
|
||||||
No doubt we could get another two to five times improvement with good
|
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
|
assembly code—but that's dwarfed by a 30-times improvement, so
|
||||||
|
|
|
||||||
15
18-01.md
15
18-01.md
File diff suppressed because one or more lines are too long
17
19-02.md
17
19-02.md
|
|
@ -10,9 +10,11 @@ chapter: '19'
|
||||||
pages: 373-375
|
pages: 373-375
|
||||||
---
|
---
|
||||||
|
|
||||||
------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
> 
|
||||||
 *The Pentium, on the other hand, has two separate 8K caches, one for code and one for data, so code prefetches can never collide with data fetches; the prefetch queue can stall only when the code being fetched isn't in the internal code cache.*
|
> The Pentium, on the other hand, has two separate 8K caches, one for code
|
||||||
------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
> and one for data, so code prefetches can never collide with data
|
||||||
|
> fetches; the prefetch queue can stall only when the code being fetched
|
||||||
|
> isn't in the internal code cache.
|
||||||
|
|
||||||
(And yes, self-modifying code still works; as with all Pentium changes,
|
(And yes, self-modifying code still works; as with all Pentium changes,
|
||||||
the dual caches introduce no incompatibilities with 386/486 code.) Also,
|
the dual caches introduce no incompatibilities with 386/486 code.) Also,
|
||||||
|
|
@ -40,9 +42,12 @@ bits, and runs twice as fast, at 66 MHz, so the Pentium can fetch both
|
||||||
instruction and data bytes from the external cache four times as fast as
|
instruction and data bytes from the external cache four times as fast as
|
||||||
the 486.
|
the 486.
|
||||||
|
|
||||||
------------------- ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
> 
|
||||||
 *Even when the Pentium is running flat-out with both pipes in use, it can generally consume only about twice as many bytes as the 486; so the ratio of external memory bandwidth to processing power is much improved, although real-world performance is heavily dependent on the size and speed of the external cache.*
|
> Even when the Pentium is running flat-out with both pipes in use, it can
|
||||||
------------------- ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
> generally consume only about twice as many bytes as the 486; so the
|
||||||
|
> ratio of external memory bandwidth to processing power is much improved,
|
||||||
|
> although real-world performance is heavily dependent on the size and
|
||||||
|
> speed of the external cache.
|
||||||
|
|
||||||
The upshot of all this is that at the same clock speed, with code and
|
The upshot of all this is that at the same clock speed, with code and
|
||||||
data that are mostly in the internal caches, the Pentium maxes out
|
data that are mostly in the internal caches, the Pentium maxes out
|
||||||
|
|
|
||||||
22
19-03.md
22
19-03.md
File diff suppressed because one or more lines are too long
20
19-04.md
20
19-04.md
|
|
@ -22,9 +22,13 @@ the branch or fall-through takes 4 or 5 cycles (if it executes in the U-
|
||||||
or V-pipe, respectively)—1 or 2 cycles more than a branch and 3 or 4
|
or V-pipe, respectively)—1 or 2 cycles more than a branch and 3 or 4
|
||||||
cycles more than a fall-through on the 486.
|
cycles more than a fall-through on the 486.
|
||||||
|
|
||||||
------------------- -----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
> 
|
||||||
 *Branch prediction is unprecedented in the x86, and fundamentally alters the nature of pedal-to-the-metal optimization, for the simple reason that it renders unrolled loops largely obsolete. Rare indeed is the loop that can't afford to spare even 1 or 0 (yes, zero!) cycles per iteration for loop counting, and that's how low the cost can go for maintaining a loop on the Pentium.*
|
> Branch prediction is unprecedented in the x86, and fundamentally alters
|
||||||
------------------- -----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
> the nature of pedal-to-the-metal optimization, for the simple reason
|
||||||
|
> that it renders unrolled loops largely obsolete. Rare indeed is the loop
|
||||||
|
> that can't afford to spare even 1 or 0 (yes, zero!) cycles per iteration
|
||||||
|
> for loop counting, and that's how low the cost can go for maintaining a
|
||||||
|
> loop on the Pentium.
|
||||||
|
|
||||||
Also, unrolled loops are bigger than normal loops, so there are extra
|
Also, unrolled loops are bigger than normal loops, so there are extra
|
||||||
(and expensive) cache misses the first time through the loop if the
|
(and expensive) cache misses the first time through the loop if the
|
||||||
|
|
@ -58,9 +62,13 @@ count on a branch to take 1 cycle when it falls through, but on the
|
||||||
Pentium you can't be sure whether it will take 1 or either 4 or 5 cycles
|
Pentium you can't be sure whether it will take 1 or either 4 or 5 cycles
|
||||||
on any given iteration.
|
on any given iteration.
|
||||||
|
|
||||||
------------------- ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
> 
|
||||||
 *As things currently stand, branch prediction is an annoyance for assembly language optimization because it's impossible to be certain exactly how code will perform until you measure it, and even then it's difficult to be sure exactly where the cycles went. All I can say is try to fall through branches if possible, and try to be consistent in your branching if not.*
|
> As things currently stand, branch prediction is an annoyance for
|
||||||
------------------- ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
> assembly language optimization because it's impossible to be certain
|
||||||
|
> exactly how code will perform until you measure it, and even then it's
|
||||||
|
> difficult to be sure exactly where the cycles went. All I can say is try
|
||||||
|
> to fall through branches if possible, and try to be consistent in your
|
||||||
|
> branching if not.
|
||||||
|
|
||||||
### Miscellaneous Pentium Topics {#Heading9}
|
### Miscellaneous Pentium Topics {#Heading9}
|
||||||
|
|
||||||
|
|
|
||||||
10
20-01.md
10
20-01.md
|
|
@ -120,9 +120,13 @@ because those instructions that the V-pipe can handle are able to pair
|
||||||
only with certain U-pipe instructions. For example, **MOVSD** uses both
|
only with certain U-pipe instructions. For example, **MOVSD** uses both
|
||||||
pipes, so no instruction can be executed in parallel with **MOVSD**.
|
pipes, so no instruction can be executed in parallel with **MOVSD**.
|
||||||
|
|
||||||
------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
> 
|
||||||
 *The use of both pipes does make **MOVSD** nearly twice as fast on the Pentium as on the 486, but it's nonetheless slower than using equivalent simpler instructions that allow for superscalar execution. Stick to the Pentium's RISC-like instructions—the pairable instructions I'll discuss next—when you're seeking maximum performance, with just a few exceptions such as **REP MOVS** and **REP STOS**.*
|
> The use of both pipes does make **MOVSD** nearly twice as fast on the
|
||||||
------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
> Pentium as on the 486, but it's nonetheless slower than using equivalent
|
||||||
|
> simpler instructions that allow for superscalar execution. Stick to the
|
||||||
|
> Pentium's RISC-like instructions—the pairable instructions I'll discuss
|
||||||
|
> next—when you're seeking maximum performance, with just a few exceptions
|
||||||
|
> such as **REP MOVS** and **REP STOS**.
|
||||||
|
|
||||||
Trickier yet, register contention can shut down the V-pipe on any given
|
Trickier yet, register contention can shut down the V-pipe on any given
|
||||||
cycle, and Address Generation Interlocks (AGIs) can stall either pipe at
|
cycle, and Address Generation Interlocks (AGIs) can stall either pipe at
|
||||||
|
|
|
||||||
19
20-02.md
19
20-02.md
|
|
@ -140,9 +140,14 @@ in Figure 20.3—a full cycle *faster* than **PUSH [*mem*]**, which takes
|
||||||
V-pipe-executable instructions to execute simultaneously (pair) in the
|
V-pipe-executable instructions to execute simultaneously (pair) in the
|
||||||
V-pipe.**
|
V-pipe.**
|
||||||
|
|
||||||
------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
> 
|
||||||
 *A fundamental rule of Pentium optimization is that it pays to break complex instructions into equivalent simple instructions, then shuffle the simple instructions for maximum use of the V-pipe. This is true partly because most of the pairable instructions are simple instructions, and partly because breaking instructions into pieces allows more freedom to rearrange code to avoid the AGIs and register contention I'll discuss in the next chapter.*
|
> A fundamental rule of Pentium optimization is that it pays to break
|
||||||
------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
> complex instructions into equivalent simple instructions, then shuffle
|
||||||
|
> the simple instructions for maximum use of the V-pipe. This is true
|
||||||
|
> partly because most of the pairable instructions are simple
|
||||||
|
> instructions, and partly because breaking instructions into pieces
|
||||||
|
> allows more freedom to rearrange code to avoid the AGIs and register
|
||||||
|
> contention I'll discuss in the next chapter.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
|
@ -175,6 +180,8 @@ proper sequencing, interleaving the simple instructions with other
|
||||||
instructions that don't use EDX or **Mem Var**, the three-instruction
|
instructions that don't use EDX or **Mem Var**, the three-instruction
|
||||||
sequence can be reduced to 1.5 cycles, but it is *14* bytes long.
|
sequence can be reduced to 1.5 cycles, but it is *14* bytes long.
|
||||||
|
|
||||||
------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
> 
|
||||||
 *It's not unusual for Pentium optimization to approximately double both performance and code size at the same time. In an important loop, go for performance and ignore the size, but on a program-wide basis, the size bears watching.*
|
> It's not unusual for Pentium optimization to approximately double both
|
||||||
------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
> performance and code size at the same time. In an important loop, go for
|
||||||
|
> performance and ignore the size, but on a program-wide basis, the size
|
||||||
|
> bears watching.
|
||||||
|
|
|
||||||
8
20-03.md
8
20-03.md
|
|
@ -39,9 +39,11 @@ fully overlap, as described below). The logical conclusion would seem to
|
||||||
be that we should strive to pair instructions of the same lengths, but
|
be that we should strive to pair instructions of the same lengths, but
|
||||||
that is often not correct.
|
that is often not correct.
|
||||||
|
|
||||||
------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
> 
|
||||||
 *The actual rule is that we should strive to pair one-cycle instructions (or, at most, two-cycle instructions, but not three-cycle instructions), which in turn leads to the corollary that we should, in general, use mostly one-cycle instructions when optimizing.*
|
> The actual rule is that we should strive to pair one-cycle instructions
|
||||||
------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
> (or, at most, two-cycle instructions, but not three-cycle instructions),
|
||||||
|
> which in turn leads to the corollary that we should, in general, use
|
||||||
|
> mostly one-cycle instructions when optimizing.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
|
|
||||||
10
20-04.md
10
20-04.md
|
|
@ -78,9 +78,13 @@ complex memory operation; in pre-superscalar days, we used to employ
|
||||||
those handy CISC memory instructions to do all that stuff without using
|
those handy CISC memory instructions to do all that stuff without using
|
||||||
any extra registers.
|
any extra registers.
|
||||||
|
|
||||||
------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
> 
|
||||||
 *More problematic still is that for maximum pairing, you'll typically have two operations proceeding at once, one in each pipe, and trying to keep two operations in registers at once is difficult indeed. There's not much to be done about this, other than clever and Spartan register usage, but be aware that it's a major element of Pentium performance programming.*
|
> More problematic still is that for maximum pairing, you'll typically
|
||||||
------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
> have two operations proceeding at once, one in each pipe, and trying to
|
||||||
|
> keep two operations in registers at once is difficult indeed. There's
|
||||||
|
> not much to be done about this, other than clever and Spartan register
|
||||||
|
> usage, but be aware that it's a major element of Pentium performance
|
||||||
|
> programming.
|
||||||
|
|
||||||
Also be aware that prefixes of every sort, with the sole exception of
|
Also be aware that prefixes of every sort, with the sole exception of
|
||||||
the 0FH prefix on non-short conditional jumps, always execute in the
|
the 0FH prefix on non-short conditional jumps, always execute in the
|
||||||
|
|
|
||||||
56
23-02.md
56
23-02.md
|
|
@ -98,29 +98,33 @@ fixed. Moreover, a great deal of graphics software now uses word
|
||||||
**OUT**s, so any computer or VGA that doesn't properly support word
|
**OUT**s, so any computer or VGA that doesn't properly support word
|
||||||
**OUT**s could scarcely be considered a clone at all.
|
**OUT**s could scarcely be considered a clone at all.
|
||||||
|
|
||||||
------------------- ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
> 
|
||||||
 *A speed tip: The setting of each chip's Index register remains the same until it is reprogrammed. This means that in cases where you are setting the same internal register repeatedly, you can set the Index register to point to that internal register once, then write to the Data register multiple times. For example, the Bit Mask register (GC register 8) is often set repeatedly inside a loop when drawing lines. The standard code for this is:*
|
> A speed tip: The setting of each chip's Index register remains the same
|
||||||
------------------- ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
> until it is reprogrammed. This means that in cases where you are setting
|
||||||
|
> the same internal register repeatedly, you can set the Index register to
|
||||||
MOV DX,03CEH ;point to GC Index register
|
> point to that internal register once, then write to the Data register
|
||||||
MOV AL,8 ;internal index of Bit Mask register
|
> multiple times. For example, the Bit Mask register (GC register 8) is
|
||||||
OUT DX,AX ;AH contains Bit Mask register setting
|
> often set repeatedly inside a loop when drawing lines. The standard code
|
||||||
|
> for this is:
|
||||||
-- ------------------------------------------------------------------------------------------------------
|
>
|
||||||
*Alternatively, the GC Index register could initially be set to point to the Bit Mask register with*
|
> MOV DX,03CEH ;point to GC Index register
|
||||||
-- ------------------------------------------------------------------------------------------------------
|
> MOV AL,8 ;internal index of Bit Mask register
|
||||||
|
> OUT DX,AX ;AH contains Bit Mask register setting
|
||||||
MOV DX,03CEH ;point to GC Index register
|
>
|
||||||
MOV AL,8 ;internal index of Bit Mask register
|
> Alternatively, the GC Index register could initially be set to point to
|
||||||
OUT DX,AL ;set GC Index register
|
> the Bit Mask register with
|
||||||
INC DX ;point to GC Data register
|
>
|
||||||
|
> MOV DX,03CEH ;point to GC Index register
|
||||||
-- -------------------------------------------------------------------------------------------------
|
> MOV AL,8 ;internal index of Bit Mask register
|
||||||
*and then the Bit Mask register could be set repeatedly with the byte-size **OUT** instruction*
|
> OUT DX,AL ;set GC Index register
|
||||||
-- -------------------------------------------------------------------------------------------------
|
> INC DX ;point to GC Data register>
|
||||||
|
>
|
||||||
OUT DX,AL ;AL contains Bit Mask register setting
|
> and then the Bit Mask register could be set repeatedly with the
|
||||||
|
> byte-size **OUT** instruction
|
||||||
-- ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
>
|
||||||
*which is generally faster (and never slower) than a word-sized **OUT**, and which does not require AH to be set, freeing up a register. Of course, this method only works if the GC Index register remains unchanged throughout the loop.*
|
> OUT DX,AL ;AL contains Bit Mask register setting
|
||||||
-- ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
>
|
||||||
|
> which is generally faster (and never slower) than a word-sized **OUT**,
|
||||||
|
> and which does not require AH to be set, freeing up a register. Of
|
||||||
|
> course, this method only works if the GC Index register remains
|
||||||
|
> unchanged throughout the loop.
|
||||||
10
23-04.md
10
23-04.md
|
|
@ -107,6 +107,10 @@ just to be safe.) Immediately after setting palette RAM, however, 20h
|
||||||
Index register to restore normal video, and at all other times bit 5
|
Index register to restore normal video, and at all other times bit 5
|
||||||
should be set to 1.
|
should be set to 1.
|
||||||
|
|
||||||
------------------- --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
> 
|
||||||
 *By the way, palette RAM can be set via the BIOS video interrupt (interrupt 10H), function 10H. Whenever an VGA function can be performed reasonably well through a BIOS function, as it can in the case of setting palette RAM, it should be, both because there is no point in reinventing the wheel and because the BIOS may well mask incompatibilities between the IBM VGA and VGA clones.*
|
> By the way, palette RAM can be set via the BIOS video interrupt
|
||||||
------------------- --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
> (interrupt 10H), function 10H. Whenever an VGA function can be performed
|
||||||
|
> reasonably well through a BIOS function, as it can in the case of
|
||||||
|
> setting palette RAM, it should be, both because there is no point in
|
||||||
|
> reinventing the wheel and because the BIOS may well mask
|
||||||
|
> incompatibilities between the IBM VGA and VGA clones.
|
||||||
21
23-06.md
21
23-06.md
File diff suppressed because one or more lines are too long
15
24-03.md
15
24-03.md
File diff suppressed because one or more lines are too long
13
25-03.md
13
25-03.md
File diff suppressed because one or more lines are too long
14
25-06.md
14
25-06.md
File diff suppressed because one or more lines are too long
13
26-03.md
13
26-03.md
File diff suppressed because one or more lines are too long
8
27-01.md
8
27-01.md
|
|
@ -94,9 +94,11 @@ register is set to 1.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
> 
|
||||||
 *It's worth noting two differences between write mode 2 and write mode 0, the standard write mode of the VGA. First, rotation of the CPU data byte does not take place in write mode 2. Second, the Set/Reset and Enable Set/Reset registers have no effect in write mode 2.*
|
> It's worth noting two differences between write mode 2 and write mode 0,
|
||||||
------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
> the standard write mode of the VGA. First, rotation of the CPU data byte
|
||||||
|
> does not take place in write mode 2. Second, the Set/Reset and Enable
|
||||||
|
> Set/Reset registers have no effect in write mode 2.
|
||||||
|
|
||||||
Now that we understand the mechanics of write mode 2, we can step back
|
Now that we understand the mechanics of write mode 2, we can step back
|
||||||
and get a feel for what it might be useful for. View bits 3-0 of the CPU
|
and get a feel for what it might be useful for. View bits 3-0 of the CPU
|
||||||
|
|
|
||||||
11
27-03.md
11
27-03.md
File diff suppressed because one or more lines are too long
15
28-03.md
15
28-03.md
File diff suppressed because one or more lines are too long
25
29-03.md
25
29-03.md
File diff suppressed because one or more lines are too long
11
29-05.md
11
29-05.md
|
|
@ -20,9 +20,14 @@ programmed to any of the 64 possible colors by either setting Attribute
|
||||||
Controller register 11H directly or calling video function 10H,
|
Controller register 11H directly or calling video function 10H,
|
||||||
subfunction 1.
|
subfunction 1.
|
||||||
|
|
||||||
------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
> 
|
||||||
 *On ECD-compatible monitors, however, there's too little scan time to display a proper border when the EGA is in 350-scan-line mode, so overscan should always be 0 (black) unless you're in 200-scanmode. Note, though, that a VGA can easily display a border on a VGA-compatible monitor, and VGAs are in fact programmed at mode set for an 8-pixel-wide border in all modes; all you need do is set the overscan color on any VGA to see the border.*
|
> On ECD-compatible monitors, however, there's too little scan time to
|
||||||
------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
> display a proper border when the EGA is in 350-scan-line mode, so
|
||||||
|
> overscan should always be 0 (black) unless you're in 200-scanmode. Note,
|
||||||
|
> though, that a VGA can easily display a border on a VGA-compatible
|
||||||
|
> monitor, and VGAs are in fact programmed at mode set for an 8-pixel-wide
|
||||||
|
> border in all modes; all you need do is set the overscan color on any
|
||||||
|
> VGA to see the border.
|
||||||
|
|
||||||
### A Bonus Blanker {#Heading6}
|
### A Bonus Blanker {#Heading6}
|
||||||
|
|
||||||
|
|
|
||||||
18
30-03.md
18
30-03.md
|
|
@ -57,9 +57,12 @@ screen start scan line is set—that seems to me to be an advantage—but
|
||||||
because the changed screen can appear *before* the new split screen
|
because the changed screen can appear *before* the new split screen
|
||||||
start scan line is set.
|
start scan line is set.
|
||||||
|
|
||||||
------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
> 
|
||||||
 *Remember, the split screen start scan line is spread out over two or three registers. What if the incompletely-changed value matches the current scan line after you've set one register but before you've set the rest? For one frame, you'll see the split screen in a wrong place—possibly a very wrong place—resulting in jumping and flicker.*
|
> Remember, the split screen start scan line is spread out over two or
|
||||||
------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
> three registers. What if the incompletely-changed value matches the
|
||||||
|
> current scan line after you've set one register but before you've set
|
||||||
|
> the rest? For one frame, you'll see the split screen in a wrong
|
||||||
|
> place—possibly a very wrong place—resulting in jumping and flicker.
|
||||||
|
|
||||||
The solution is simple: Set the split screen start scan line at a time
|
The solution is simple: Set the split screen start scan line at a time
|
||||||
when it can't possibly match the currently displayed scan line. The easy
|
when it can't possibly match the currently displayed scan line. The easy
|
||||||
|
|
@ -103,9 +106,12 @@ designing split-screen images that might be displayed on EGAs, and you
|
||||||
should in any case check how your split-screens look on both VGAs and
|
should in any case check how your split-screens look on both VGAs and
|
||||||
EGAs.
|
EGAs.
|
||||||
|
|
||||||
------------------- ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
> 
|
||||||
 *I have an important caution here: Don't count on the EGA's split-screen bug; that is, don't rely on the first scan line being doubled when you design your split screens. IBM designed and made the original EGA, but a lot of companies cloned it, and there's no guarantee that all EGA clones copy the bug. It is a certainty, at least, that the VGA didn't copy it.*
|
> I have an important caution here: Don't count on the EGA's split-screen
|
||||||
------------------- ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
> bug; that is, don't rely on the first scan line being doubled when you
|
||||||
|
> design your split screens. IBM designed and made the original EGA, but a
|
||||||
|
> lot of companies cloned it, and there's no guarantee that all EGA clones
|
||||||
|
> copy the bug. It is a certainty, at least, that the VGA didn't copy it.
|
||||||
|
|
||||||
There's another respect in which the EGA is inferior to the VGA when it
|
There's another respect in which the EGA is inferior to the VGA when it
|
||||||
comes to the split screen, and that's in the area of panning when the
|
comes to the split screen, and that's in the area of panning when the
|
||||||
|
|
|
||||||
8
30-04.md
8
30-04.md
|
|
@ -36,9 +36,11 @@ current byte. As the top part of the screen moves smoothly about, the
|
||||||
split screen will move and jump, move and jump, over and over. Believe
|
split screen will move and jump, move and jump, over and over. Believe
|
||||||
me, it's not a pretty sight.
|
me, it's not a pretty sight.
|
||||||
|
|
||||||
------------------- -----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
> 
|
||||||
 *What's to be done? On the EGA, nothing. Unless you're willing to have your users' eyes doing the jitterbug, don't use horizontal smooth scrolling while the split screen is up. Byte panning is fine—just don't change the Pel Panning register from its default setting.*
|
> What's to be done? On the EGA, nothing. Unless you're willing to have
|
||||||
------------------- -----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
> your users' eyes doing the jitterbug, don't use horizontal smooth
|
||||||
|
> scrolling while the split screen is up. Byte panning is fine—just don't
|
||||||
|
> change the Pel Panning register from its default setting.
|
||||||
|
|
||||||
On the VGA, there is recourse. A VGA-only bit, bit 5 of the AC Mode
|
On the VGA, there is recourse. A VGA-only bit, bit 5 of the AC Mode
|
||||||
Control register (AC register 10H), turns off pel panning in the split
|
Control register (AC register 10H), turns off pel panning in the split
|
||||||
|
|
|
||||||
11
30-06.md
11
30-06.md
|
|
@ -62,9 +62,14 @@ I'm sure Richard is right when it comes to the real McCoy IBM VGA and
|
||||||
EGA, but I'm less confident that every clone out there loads the start
|
EGA, but I'm less confident that every clone out there loads the start
|
||||||
address at the start of vertical sync.
|
address at the start of vertical sync.
|
||||||
|
|
||||||
------------------- ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
> 
|
||||||
 *For that very reason, I generally advise people not to use horizontal smooth panning unless they can test their software on all the makes of display adapter it might run on. I've used Richard's approach in Listings 30.1 and 30.2, and so far as I've seen it works fine, but be aware that there are potential, albeit unproven, hazards to relying on the setting of the start address registers to occur at a specific time in the frame.*
|
> For that very reason, I generally advise people not to use horizontal
|
||||||
------------------- ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
> smooth panning unless they can test their software on all the makes of
|
||||||
|
> display adapter it might run on. I've used Richard's approach in
|
||||||
|
> Listings 30.1 and 30.2, and so far as I've seen it works fine, but be
|
||||||
|
> aware that there are potential, albeit unproven, hazards to relying on
|
||||||
|
> the setting of the start address registers to occur at a specific time
|
||||||
|
> in the frame.
|
||||||
|
|
||||||
The interaction of the start address registers and the Pel Panning
|
The interaction of the start address registers and the Pel Panning
|
||||||
register is worthy of note. After waiting for the end of vertical sync
|
register is worthy of note. After waiting for the end of vertical sync
|
||||||
|
|
|
||||||
11
32-03.md
11
32-03.md
|
|
@ -217,6 +217,11 @@ The first thing you'll notice when you run this code is that the speed
|
||||||
of 360x480 256-color mode is pretty good, especially considering that
|
of 360x480 256-color mode is pretty good, especially considering that
|
||||||
most of the program is im-plemented in C.
|
most of the program is im-plemented in C.
|
||||||
|
|
||||||
------------------- ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
> 
|
||||||
 *Drawing in 360x480 256-color mode can sometimes actually be faster than in the 16-color modes, because the byte-per-pixel display memory organization of 256-color mode eliminates the need to read display memory before writing to it in order to isolate individual pixels coexisting within a single byte. In addition, 360x480 256-color mode is a variant of Mode X, which we'll encounter in detail in Chapter 47, and supports all the high-performance features of Mode X.*
|
> Drawing in 360x480 256-color mode can sometimes actually be faster than
|
||||||
------------------- ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
> in the 16-color modes, because the byte-per-pixel display memory
|
||||||
|
> organization of 256-color mode eliminates the need to read display
|
||||||
|
> memory before writing to it in order to isolate individual pixels
|
||||||
|
> coexisting within a single byte. In addition, 360x480 256-color mode is
|
||||||
|
> a variant of Mode X, which we'll encounter in detail in Chapter 47, and
|
||||||
|
> supports all the high-performance features of Mode X.
|
||||||
14
34-02.md
14
34-02.md
File diff suppressed because one or more lines are too long
15
34-04.md
15
34-04.md
File diff suppressed because one or more lines are too long
14
35-04.md
14
35-04.md
File diff suppressed because one or more lines are too long
10
37-02.md
10
37-02.md
|
|
@ -55,9 +55,13 @@ When I compared the C and assembly implementations drawing to normal
|
||||||
system (nondisplay) memory, I found that the assembly code was actually
|
system (nondisplay) memory, I found that the assembly code was actually
|
||||||
four times as fast as the C code.
|
four times as fast as the C code.
|
||||||
|
|
||||||
------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
> 
|
||||||
 *In fact, Listing 37.1 draws VGA lines at about 92 percent of the maximum possible rate in my system—that is, it draws very nearly as fast as the VGA hardware will allow. All the optimization in the world would get me less than 10 percent faster line drawing—and only if I eliminated all overhead, an unlikely proposition at best. The code isn't fully optimized, but so what?*
|
> In fact, Listing 37.1 draws VGA lines at about 92 percent of the maximum
|
||||||
------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
> possible rate in my system—that is, it draws very nearly as fast as the
|
||||||
|
> VGA hardware will allow. All the optimization in the world would get me
|
||||||
|
> less than 10 percent faster line drawing—and only if I eliminated all
|
||||||
|
> overhead, an unlikely proposition at best. The code isn't fully
|
||||||
|
> optimized, but so what?
|
||||||
|
|
||||||
Now it's true that faster line-drawing code would likely be more
|
Now it's true that faster line-drawing code would likely be more
|
||||||
beneficial on faster VGAs, especially local-bus VGAs, and in slower
|
beneficial on faster VGAs, especially local-bus VGAs, and in slower
|
||||||
|
|
|
||||||
11
38-02.md
11
38-02.md
|
|
@ -44,9 +44,14 @@ range around a pixel, then that pixel will be drawn once and only
|
||||||
once—just what we need in order to be able to fit filled polygons
|
once—just what we need in order to be able to fit filled polygons
|
||||||
together seamlessly.
|
together seamlessly.
|
||||||
|
|
||||||
------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
> 
|
||||||
 *This sort of non-overlapping polygon filling isn't ideal for all purposes. Polygons are skewed toward the top and left edges, which not only introduces drawing error relative to the ideal polygon but also means that a filled polygon won't match the same polygon drawn unfilled. Narrow wedges and one-pixel-wide polygons will show up spottily. All in all, the choice of polygon-filling approach depends entirely on the ways in which the filled polygons must be used.*
|
> This sort of non-overlapping polygon filling isn't ideal for all
|
||||||
------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
> purposes. Polygons are skewed toward the top and left edges, which not
|
||||||
|
> only introduces drawing error relative to the ideal polygon but also
|
||||||
|
> means that a filled polygon won't match the same polygon drawn unfilled.
|
||||||
|
> Narrow wedges and one-pixel-wide polygons will show up spottily. All in
|
||||||
|
> all, the choice of polygon-filling approach depends entirely on the ways
|
||||||
|
> in which the filled polygons must be used.
|
||||||
|
|
||||||
For our purposes, nonoverlapping polygons are the way to go, so let's
|
For our purposes, nonoverlapping polygons are the way to go, so let's
|
||||||
have at them.
|
have at them.
|
||||||
|
|
|
||||||
6
39-02.md
6
39-02.md
|
|
@ -125,6 +125,6 @@ the floating point results were sufficiently imprecise to creep from
|
||||||
just under an integer value to just over it, so that the **ceil**
|
just under an integer value to just over it, so that the **ceil**
|
||||||
function returned a coordinate that was one too large.
|
function returned a coordinate that was one too large.
|
||||||
|
|
||||||
------------------- ---------------------------------------------------------------------------------------------------------
|
> 
|
||||||
 *Floating point is very accurate—but it is not precise. Integer calculations, properly performed, are.*
|
> Floating point is very accurate—but it is not precise. Integer
|
||||||
------------------- ---------------------------------------------------------------------------------------------------------
|
> calculations, properly performed, are.
|
||||||
17
39-04.md
17
39-04.md
File diff suppressed because one or more lines are too long
8
40-05.md
8
40-05.md
|
|
@ -23,9 +23,11 @@ back links and insertion sorting aren't great enough; profiling a
|
||||||
variety of polygons reveals that less than ten percent of total time is
|
variety of polygons reveals that less than ten percent of total time is
|
||||||
spent sorting the AET.
|
spent sorting the AET.
|
||||||
|
|
||||||
------------------- -----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
> 
|
||||||
 *The potential 1 to 5 percent speedup gained by optimizing AET sorting just isn't worth it in any but the most demanding application—a good example of the need to keep an overall perspective when comparing the theoretical characteristics of various approaches.*
|
> The potential 1 to 5 percent speedup gained by optimizing AET sorting
|
||||||
------------------- -----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
> just isn't worth it in any but the most demanding application—a good
|
||||||
|
> example of the need to keep an overall perspective when comparing the
|
||||||
|
> theoretical characteristics of various approaches.
|
||||||
|
|
||||||
### Nonconvex Polygons {#Heading8}
|
### Nonconvex Polygons {#Heading8}
|
||||||
|
|
||||||
|
|
|
||||||
12
41-01.md
12
41-01.md
File diff suppressed because one or more lines are too long
12
42-05.md
12
42-05.md
File diff suppressed because one or more lines are too long
13
44-04.md
13
44-04.md
File diff suppressed because one or more lines are too long
18
44-05.md
18
44-05.md
File diff suppressed because one or more lines are too long
11
45-02.md
11
45-02.md
|
|
@ -36,11 +36,12 @@ memory tends not to be as bad as VGA I/O, but lord knows it isn't
|
||||||
Table: Table 45.1 Results of I/O performance tests run under the Phar
|
Table: Table 45.1 Results of I/O performance tests run under the Phar
|
||||||
Lap386|DOS-Extender.
|
Lap386|DOS-Extender.
|
||||||
|
|
||||||
* * * * *
|
> 
|
||||||
|
> **OUT**s, in general, are lousy on the 486 (and to think they only took
|
||||||
------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
> three cycles on the 286!). **OUT**s to VGAs are particularly lousy.
|
||||||
 ***OUT**s, in general, are lousy on the 486 (and to think they only took three cycles on the 286!). **OUT**s to VGAs are particularly lousy. Display memory performance is pretty poor, especially for reads. The conclusions are obvious, I would hope. Structure your graphics code, and, in general, all 486 code, to avoid **OUT**s.*
|
> Display memory performance is pretty poor, especially for reads. The
|
||||||
------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
> conclusions are obvious, I would hope. Structure your graphics code,
|
||||||
|
> and, in general, all 486 code, to avoid **OUT**s.
|
||||||
|
|
||||||
For graphics, this especially means using write mode 3 rather than the
|
For graphics, this especially means using write mode 3 rather than the
|
||||||
bit-mask register. When you must use the bit mask, arrange drawing so
|
bit-mask register. When you must use the bit mask, arrange drawing so
|
||||||
|
|
|
||||||
10
45-04.md
10
45-04.md
|
|
@ -22,9 +22,13 @@ bitmap into the system-memory buffer, then copy the buffer to the
|
||||||
screen, and the effect will be of having panned the window to the new
|
screen, and the effect will be of having panned the window to the new
|
||||||
location.
|
location.
|
||||||
|
|
||||||
------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
> 
|
||||||
 *Another argument in favor of a small viewing window is that it restricts the amount of display memory actually drawn to. Restricting the display memory used for animation reduces the total number of display-memory accesses, which in turn boosts overall performance; it also improves the performance and appearance of panning, in which the whole window has to be redrawn or copied.*
|
> Another argument in favor of a small viewing window is that it restricts
|
||||||
------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
> the amount of display memory actually drawn to. Restricting the display
|
||||||
|
> memory used for animation reduces the total number of display-memory
|
||||||
|
> accesses, which in turn boosts overall performance; it also improves the
|
||||||
|
> performance and appearance of panning, in which the whole window has to
|
||||||
|
> be redrawn or copied.
|
||||||
|
|
||||||
If you keep a close watch, you'll notice that many high-performance
|
If you keep a close watch, you'll notice that many high-performance
|
||||||
animation games similarly restrict their full-featured animation area to
|
animation games similarly restrict their full-featured animation area to
|
||||||
|
|
|
||||||
12
47-05.md
12
47-05.md
File diff suppressed because one or more lines are too long
10
48-03.md
10
48-03.md
|
|
@ -83,6 +83,10 @@ set the Read Map register to select a source plane and the Map Mask
|
||||||
register to select the corresponding destination plane. Then, copy all
|
register to select the corresponding destination plane. Then, copy all
|
||||||
pixels in that plane, repeating for all four planes.)
|
pixels in that plane, repeating for all four planes.)
|
||||||
|
|
||||||
------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
> 
|
||||||
 *Although copying through the latches is, in general, a speedy technique, especially on slower VGAs, it's not always a win. Reading video memory tends to be quite a bit slower than writing, and on a fast VLB or PCI adapter, it can be faster to copy from main memory to display memory than it is to copy from display memory to display memory via the latches.*
|
> Although copying through the latches is, in general, a speedy technique,
|
||||||
------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
> especially on slower VGAs, it's not always a win. Reading video memory
|
||||||
|
> tends to be quite a bit slower than writing, and on a fast VLB or PCI
|
||||||
|
> adapter, it can be faster to copy from main memory to display memory
|
||||||
|
> than it is to copy from display memory to display memory via the
|
||||||
|
> latches.
|
||||||
12
49-03.md
12
49-03.md
File diff suppressed because one or more lines are too long
8
51-01.md
8
51-01.md
|
|
@ -29,9 +29,11 @@ This involves perspective, shading, proper handling of hidden surfaces,
|
||||||
and rapid and smooth screen updates; the whole deal is considerably more
|
and rapid and smooth screen updates; the whole deal is considerably more
|
||||||
difficult to pull off on a PC than 2-D animation.
|
difficult to pull off on a PC than 2-D animation.
|
||||||
|
|
||||||
------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
> 
|
||||||
 *In some senses, however, 3-D animation is easier than 2-D. Because there's more going on in 3-D animation, the eye and brain tend to make more assumptions, and so are more apt to see what they expect to see, rather than what's actually there.*
|
> In some senses, however, 3-D animation is easier than 2-D. Because
|
||||||
------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
> there's more going on in 3-D animation, the eye and brain tend to make
|
||||||
|
> more assumptions, and so are more apt to see what they expect to see,
|
||||||
|
> rather than what's actually there.
|
||||||
|
|
||||||
If you're piloting a (virtual) ship through a field of thousands of
|
If you're piloting a (virtual) ship through a field of thousands of
|
||||||
asteroids at high speed, you're unlikely to notice if the more distant
|
asteroids at high speed, you're unlikely to notice if the more distant
|
||||||
|
|
|
||||||
15
56-03.md
15
56-03.md
File diff suppressed because one or more lines are too long
10
57-01.md
10
57-01.md
|
|
@ -68,9 +68,13 @@ VGA, I found that the faster rotation was too fast! The ball spun so
|
||||||
rapidly that the eye couldn't blend successive images together into
|
rapidly that the eye couldn't blend successive images together into
|
||||||
continuous motion, much like watching a badly flickering movie.
|
continuous motion, much like watching a badly flickering movie.
|
||||||
|
|
||||||
------------------- --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
> 
|
||||||
 *So the second lesson is that either too little or too much speed can destroy the illusion. Unless you're antialiasing, you need to tune the shifting of your images so that they're in the "sweet spot" of apparent motion, in which the eye is willing to ignore the jumping and aliasing, and blend the images together into continuous motion. Only experience can give you a feel for that sweet spot.*
|
> So the second lesson is that either too little or too much speed can
|
||||||
------------------- --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
> destroy the illusion. Unless you're antialiasing, you need to tune the
|
||||||
|
> shifting of your images so that they're in the "sweet spot" of apparent
|
||||||
|
> motion, in which the eye is willing to ignore the jumping and aliasing,
|
||||||
|
> and blend the images together into continuous motion. Only experience
|
||||||
|
> can give you a feel for that sweet spot.
|
||||||
|
|
||||||
#### Fixed-Point Arithmetic, Redux {#Heading4}
|
#### Fixed-Point Arithmetic, Redux {#Heading4}
|
||||||
|
|
||||||
|
|
|
||||||
16
58-03.md
16
58-03.md
File diff suppressed because one or more lines are too long
10
59-03.md
10
59-03.md
|
|
@ -130,6 +130,10 @@ of the left and right vertices indicates which way the wall is facing.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
------------------- -----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
> 
|
||||||
 *Be aware that BSP trees can often be made smaller and more efficient by detecting collinear surfaces (like aligned wall segments) and generating only one BSP node for each collinear set, with the collinear surfaces stored in, say, a linked list attached to that node. Collinear surfaces partition space identically and can't occlude one another, so it suffices to generate one splitting node for each collinear set.*
|
> Be aware that BSP trees can often be made smaller and more efficient by
|
||||||
------------------- -----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
> detecting collinear surfaces (like aligned wall segments) and generating
|
||||||
|
> only one BSP node for each collinear set, with the collinear surfaces
|
||||||
|
> stored in, say, a linked list attached to that node. Collinear surfaces
|
||||||
|
> partition space identically and can't occlude one another, so it
|
||||||
|
> suffices to generate one splitting node for each collinear set.
|
||||||
27
59-05.md
27
59-05.md
File diff suppressed because one or more lines are too long
10
61-03.md
10
61-03.md
|
|
@ -40,9 +40,13 @@ vectors that lie within the surface. This means that we can generate the
|
||||||
screenspace normals we need by taking the cross product of two adjacent
|
screenspace normals we need by taking the cross product of two adjacent
|
||||||
polygon edges, as shown in Figure 61.4.
|
polygon edges, as shown in Figure 61.4.
|
||||||
|
|
||||||
------------------- --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
> 
|
||||||
 *In fact, we can cull with only one-third the work needed to generate a full cross product; because we're interested only in the sign of the z component of the normal, we can skip entirely calculating the x and y components. The only caveat is to be careful that neither edge you choose is zero-length and that the edges aren't collinear, because the dot product can't produce a normal in those cases.*
|
> In fact, we can cull with only one-third the work needed to generate a
|
||||||
------------------- --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
> full cross product; because we're interested only in the sign of the z
|
||||||
|
> component of the normal, we can skip entirely calculating the x and y
|
||||||
|
> components. The only caveat is to be careful that neither edge you
|
||||||
|
> choose is zero-length and that the edges aren't collinear, because the
|
||||||
|
> dot product can't produce a normal in those cases.
|
||||||
|
|
||||||

|
generates a polygon normal.*](images/61-04.jpg)
|
||||||
|
|
|
||||||
13
61-04.md
13
61-04.md
File diff suppressed because one or more lines are too long
12
62-01.md
12
62-01.md
File diff suppressed because one or more lines are too long
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue