Initial conversion to markdown
This commit is contained in:
parent
a96a114403
commit
5e10794a24
362 changed files with 67353 additions and 0 deletions
122
07-01.md
Normal file
122
07-01.md
Normal file
|
|
@ -0,0 +1,122 @@
|
|||
------------------------ --------------------------------- --------------------
|
||||
[Previous](06-02.html) [Table of Contents](index.html) [Next](07-02.html)
|
||||
------------------------ --------------------------------- --------------------
|
||||
|
||||
Chapter 7\
|
||||
Local Optimization {#Heading1}
|
||||
-------------------
|
||||
|
||||
### Optimizing Halfway between Algorithms and Cycle Counting {#Heading2}
|
||||
|
||||
You might not think it, but there’s much to learn about performance
|
||||
programming from the Great Buffalo Sauna Fiasco. To wit:
|
||||
|
||||
The scene is Buffalo, New York, in the dead of winter, with the snow
|
||||
piled several feet deep. Four college students, living in typical
|
||||
student housing, are frozen to the bone. The third floor of their house,
|
||||
uninsulated and so cold that it’s uninhabitable, has an ancient
|
||||
bathroom. One fabulously cold day, inspiration strikes:
|
||||
|
||||
“Hey—we could make that bathroom into a *sauna!*”
|
||||
|
||||
Pandemonium ensues. Someone rushes out and buys a gas heater, and at
|
||||
considerable risk to life and limb hooks it up to an abandoned but still
|
||||
live gas pipe that once fed a stove on the third floor. Someone else
|
||||
gets sheets of plastic and lines the walls of the bathroom to keep the
|
||||
moisture in, and yet another student gets a bucket full of rocks. The
|
||||
remaining chap brings up some old wooden chairs and sets them up to make
|
||||
benches along the sides of the bathroom. *Voila*—instant sauna!
|
||||
|
||||
They crank up the gas heater, put the bucket of rocks in front of it,
|
||||
close the door, take off their clothes, and sit down to steam
|
||||
themselves. Mind you, it’s not yet 50 degrees Fahrenheit in this room,
|
||||
but the gas heater is roaring. Surely warmer times await.
|
||||
|
||||
Indeed they do. The temperature climbs to 55 degrees, then 60, then 63,
|
||||
then 65, and finally creeps up to 68 degrees.
|
||||
|
||||
And there it stops.
|
||||
|
||||
68 degrees is warm for an uninsulated third floor in Buffalo in the dead
|
||||
of winter. Damn warm. It is not, however, particularly warm for a sauna.
|
||||
Eventually someone acknowledges the obvious and allows that it might
|
||||
have been a stupid idea after all, and everyone agrees, and they shut
|
||||
off the heater and leave, each no doubt offering silent thanks that they
|
||||
had gotten out of this without any incidents requiring major surgery.
|
||||
|
||||
And so we see that the best idea in the world can fail for lack of
|
||||
either proper design or adequate horsepower. The primary cause of the
|
||||
Great Buffalo Sauna Fiasco was a lack of horsepower; the gas heater was
|
||||
flat-out undersized. This is analogous to trying to write programs that
|
||||
incorporate features like bitmapped text and searching of multisegment
|
||||
buffers without using high-performance assembly language. Any PC
|
||||
language can perform just about any function you can think
|
||||
of—eventually. That heater would eventually have heated the room to 110
|
||||
degrees, too—along about the first of June or so.
|
||||
|
||||
The Great Buffalo Sauna Fiasco also suffered from fundamental design
|
||||
flaws. A more powerful heater would indeed have made the room hotter—and
|
||||
might well have burned the house down in the process. Likewise, proper
|
||||
algorithm selection and good design are fundamental to performance. The
|
||||
extra horsepower a superb assembly language implementation gives a
|
||||
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.*
|
||||
------------------- ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
So, drawing fortitude from the knowledge that our quest is a pure and
|
||||
worthy one, let’s resume our exploration of assembly language
|
||||
instructions with hidden talents and instructions with well-known
|
||||
talents that are less than they appear to be. In the process, we’ll come
|
||||
to see that there is another, very important optimization level between
|
||||
the algorithm/design level and the cycle-counting/individual instruction
|
||||
level. I’ll call this middle level *local optimization;* it involves
|
||||
focusing on optimizing sequences of instructions rather than individual
|
||||
instructions, all with an eye to implementing designs as efficiently as
|
||||
possible given the capabilities of the x86 family instruction set.
|
||||
|
||||
And yes, in case you’re wondering, the above story is indeed true. Was I
|
||||
there? Let me put it this way: If I were, I’d never admit it!
|
||||
|
||||
#### When LOOP Is a Bad Idea {#Heading3}
|
||||
|
||||
Let’s examine first an instruction that is less than it appears to be:
|
||||
**LOOP**. There’s no mystery about what **LOOP** does; it decrements CX
|
||||
and branches if CX doesn’t decrement to zero. It’s so beautifully suited
|
||||
to the task of counting down loops that any experienced x86 programmer
|
||||
instinctively stuffs the loop count in CX and reaches for **LOOP** when
|
||||
setting up a loop. That’s fine—**LOOP** does, of course, work as
|
||||
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.)*
|
||||
------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
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**
|
||||
is generally a little faster still because it’s a byte shorter and so
|
||||
can be fetched faster. On the 386, however, things change; **LOOP** is
|
||||
two cycles *slower* than **DEC/JNZ,** and the fetch time for one extra
|
||||
byte on even an uncached 386 generally isn’t significant. (Remember that
|
||||
the 386 fetches four instruction bytes at a pop.) **LOOP** is three
|
||||
cycles slower than **DEC/JNZ** on the 486, and the 486 executes
|
||||
instructions in so few cycles that those three cycles mean that
|
||||
**DEC/JNZ** is nearly *twice* as fast as **LOOP**. Then, too, unlike
|
||||
**LOOP, DEC** doesn’t require that **CX** be used, so the **DEC/JNZ**
|
||||
solution is both faster and more flexible on the 386 and 486, and on the
|
||||
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
|
||||
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!*
|
||||
------------------- --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
------------------------ --------------------------------- --------------------
|
||||
[Previous](06-02.html) [Table of Contents](index.html) [Next](07-02.html)
|
||||
------------------------ --------------------------------- --------------------
|
||||
|
||||
* * * * *
|
||||
|
||||
Graphics Programming Black Book © 2001 Michael Abrash
|
||||
Loading…
Reference in a new issue