Normal quotes

This commit is contained in:
James Gregory 2013-12-30 18:34:31 +11:00
commit 18011c595a
352 changed files with 6040 additions and 6040 deletions

View file

@ -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?
------------------- ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
![](images/i.jpg) *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.*
![](images/i.jpg) *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