Normal quotes
This commit is contained in:
parent
5e10794a24
commit
18011c595a
352 changed files with 6040 additions and 6040 deletions
34
17-06.md
34
17-06.md
|
|
@ -2,10 +2,10 @@
|
|||
[Previous](17-05.html) [Table of Contents](index.html) [Next](17-07.html)
|
||||
------------------------ --------------------------------- --------------------
|
||||
|
||||
We’re still not ready for assembly, though; what we need is a new
|
||||
We're still not ready for assembly, though; what we need is a new
|
||||
perspective that lends itself to vastly better performance in C++. The
|
||||
Life program in the next section is *three to seven times* faster than
|
||||
Listing 17.4—and it’s still in C++.
|
||||
Listing 17.4—and it's still in C++.
|
||||
|
||||
How is this possible? Here are some hints:
|
||||
|
||||
|
|
@ -19,10 +19,10 @@ How is this possible? Here are some hints:
|
|||
|
||||
In the previous section, we saw how a C++ program could be sped up about
|
||||
eight times simply by rearranging the data and code in straightforward
|
||||
ways. Now we’re going to see how right-brain non-linear optimization can
|
||||
ways. Now we're going to see how right-brain non-linear optimization can
|
||||
speed things up by another four times—and make the code *simpler.*
|
||||
|
||||
Now *that’s* Zen code optimization.
|
||||
Now *that's* Zen code optimization.
|
||||
|
||||
I have two objectives to achieve in the remainder of this chapter.
|
||||
First, I want to show that optimization consists of many levels, from
|
||||
|
|
@ -43,25 +43,25 @@ optimizations were moving the critical code into a single loop and using
|
|||
pointers rather than member functions whenever possible. In other words,
|
||||
we took what we already knew and made it more efficient.
|
||||
|
||||
Now it’s time to re-examine the nature of this programming task from the
|
||||
ground up, looking for things that we *don’t* yet know. Let’s take a
|
||||
Now it's time to re-examine the nature of this programming task from the
|
||||
ground up, looking for things that we *don't* yet know. Let's take a
|
||||
moment to review what the Game of Life consists of. The basic task is
|
||||
evolving a new generation, and that’s done by looking at the number of
|
||||
“on” neighbors a cell has and the cell’s own state. If a cell is on, and
|
||||
evolving a new generation, and that's done by looking at the number of
|
||||
"on" neighbors a cell has and the cell's own state. If a cell is on, and
|
||||
two or three neighbors are on, then the cell stays on; otherwise, an
|
||||
on-cell is turned off. If a cell is off and exactly three neighbors are
|
||||
on, then the cell is turned on; otherwise, an off-cell stays off. That’s
|
||||
on, then the cell is turned on; otherwise, an off-cell stays off. That's
|
||||
all there is to it. As any fool can see, the trick is to arrange things
|
||||
so that we can count neighbors and check the cell state as quickly as
|
||||
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?
|
||||
|
||||
------------------- ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
 *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
|
||||
generations, the vast majority of cells are off. In fact, the vast
|
||||
majority of cells are not only off but are entirely surrounded by
|
||||
|
|
@ -69,7 +69,7 @@ off-cells. Also, cells change state infrequently; in any given
|
|||
generation after the first few, most cells remain in the same state as
|
||||
in the previous generation.
|
||||
|
||||
Do you see where I’m heading? Do you hear a whisper of inspiration from
|
||||
Do you see where I'm heading? Do you hear a whisper of inspiration from
|
||||
your right brain? The original implementation stored cell states as
|
||||
1-bits (on), or 0-bits (off). For each generation and for each cell, it
|
||||
counted the states of the eight neighbors, for an average of eight
|
||||
|
|
@ -96,7 +96,7 @@ only one-tenth that of the original approach!
|
|||
|
||||
#### Acting on What We Know {#Heading10}
|
||||
|
||||
Once we’ve changed the cellmap format to store neighbor counts as well
|
||||
Once we've changed the cellmap format to store neighbor counts as well
|
||||
as states, with a byte for each cell, we can get another performance
|
||||
boost by again examining what we know about our data. I said earlier
|
||||
that most cells are off during any given generation. This means that
|
||||
|
|
@ -106,13 +106,13 @@ can skip over scads of unchanged cells at a pop simply by scanning for
|
|||
non-zero bytes. This is much faster than explicitly testing cell states
|
||||
and neighbor counts, and lends itself beautifully to assembly language
|
||||
implementation as **REPZ SCASB** or (with a little cleverness) **REPZ
|
||||
SCASW.** (Unfortunately, there’s no C library function that can scan
|
||||
memory for the next byte that’s non-zero.)
|
||||
SCASW.** (Unfortunately, there's no C library function that can scan
|
||||
memory for the next byte that's non-zero.)
|
||||
|
||||
Listing 17.5 is a Game of Life implementation that uses the
|
||||
neighbor-count cell map format and scans for non-zero bytes. On a 20 MHz
|
||||
386, Listing 17.5 is about 4.5 times faster at calculating generations
|
||||
(that is, the generation engine is 4.5 times faster; I’m ignoring the
|
||||
(that is, the generation engine is 4.5 times faster; I'm ignoring the
|
||||
time consumed by drawing and text display) than Listing 17.4, which is
|
||||
no slouch. On a 33 MHz 486, Listing 17.5 is about 3.5 times faster than
|
||||
Listing 17.4. This is true even though Listing 17.5 must be compiled
|
||||
|
|
|
|||
Loading…
Reference in a new issue