Initial conversion to markdown
This commit is contained in:
parent
a96a114403
commit
5e10794a24
362 changed files with 67353 additions and 0 deletions
164
17-05.md
Normal file
164
17-05.md
Normal file
|
|
@ -0,0 +1,164 @@
|
|||
------------------------ --------------------------------- --------------------
|
||||
[Previous](17-04.html) [Table of Contents](index.html) [Next](17-06.html)
|
||||
------------------------ --------------------------------- --------------------
|
||||
|
||||
In Listing 17.3, note the padded cellmap edges, and the alteration of
|
||||
the member functions to compensate for the padding. Also note that the
|
||||
width now has to be a multiple of eight, to facilitate the process of
|
||||
copying the edges to the opposite padding bytes. We have decreased the
|
||||
generality of our Game of Life implementation in exchange for better
|
||||
performance. That’s a very common trade-off, as common as trading memory
|
||||
for performance. As a rule, the more general a program is, the slower it
|
||||
is. A corollary is that often (not always, but often), the more heavily
|
||||
optimized a program is, the more complex and the more difficult to
|
||||
implement it is. You can often improve performance a good deal by
|
||||
implementing only the level of generality you need, but at the same time
|
||||
decreased generality makes it more difficult to change or port the
|
||||
program at some later date. A Game of Life implementation, such as
|
||||
Listing 17.1, that’s built on **set\_cell()**, **clear\_cell()**, and
|
||||
**get\_cell()** is completely general; you can change the cell storage
|
||||
format simply by changing the constructor and those three functions.
|
||||
Listing 17.3 is harder to change because **count\_neighbors()** would
|
||||
also have to be altered, and it’s more complex than any of the other
|
||||
functions.
|
||||
|
||||
So, in Listing 17.3, we’ve gotten under the hood and changed the cellmap
|
||||
format a little, and gotten impressive results. But now
|
||||
**count\_neighbors()** is hard-wired for optimized counting, and it’s
|
||||
still taking up more than half the time. Maybe now it’s time to go to
|
||||
assembly?
|
||||
|
||||
Not hardly.
|
||||
|
||||
### Heavy-Duty C++ Optimization {#Heading7}
|
||||
|
||||
Before we get to assembly, we still have to perform C++ optimization,
|
||||
then see if we can find an alternative approach that better fits the
|
||||
application. It would actually have made much more sense if we had
|
||||
looked for a new approach as our first optimization step, but I decided
|
||||
it would be better to cover straightforward C++ optimizations at this
|
||||
point, and the mind-bending stuff a little later. Right now, let’s look
|
||||
at some C++ optimizations; Listing 17.4 is a C++-optimized version of
|
||||
Listing 17.3.
|
||||
|
||||
**LISTING 17.4 L17-4.CPP**
|
||||
|
||||
/* next_generation(), implemented using fast, all-in-one hard-wired
|
||||
neighbor count/update/draw function. Otherwise, the same as
|
||||
Listing 17.3. */
|
||||
|
||||
/* Calculates the next generation of current_map and stores it in
|
||||
next_map. */
|
||||
void cellmap::next_generation(cellmap& next_map)
|
||||
{
|
||||
unsigned int x, y, neighbor_count;
|
||||
unsigned int width_in_bytesX2 = width_in_bytes << 1;
|
||||
unsigned char *cell_ptr, *current_cell_ptr, mask, current_mask;
|
||||
unsigned char *base_cell_ptr, *row_cell_ptr, base_mask;
|
||||
unsigned char *dest_cell_ptr = next_map.cells;
|
||||
|
||||
// Process all cells in the current cellmap
|
||||
row_cell_ptr = cells; // point to upper left neighbor of
|
||||
// first cell in cell map
|
||||
for (y=0; y<height; y++) { // repeat for each row of cells
|
||||
// Cell pointer and cell bit mask for first cell in row
|
||||
base_cell_ptr = row_cell_ptr; // to access upper left neighbor
|
||||
base_mask = 0x01; // of first cell in row
|
||||
for (x=0; x<width; x++) { // repeat for each cell in row
|
||||
// First, count neighbors
|
||||
// Point to upper left neighbor of current cell
|
||||
cell_ptr = base_cell_ptr; // pointer and bit mask for
|
||||
mask = base_mask; // upper left neighbor
|
||||
// Count upper left neighbor
|
||||
neighbor_count = (*cell_ptr & mask) ? 1 : 0;
|
||||
// Count left neighbor
|
||||
if ((*(cell_ptr + width_in_bytes) & mask)) neighbor_count++;
|
||||
// Count lower left neighbor
|
||||
if ((*(cell_ptr + width_in_bytesX2) & mask))
|
||||
neighbor_count++;
|
||||
// Point to upper neighbor
|
||||
if ((mask >>= 1) == 0) {
|
||||
mask = 0x80;
|
||||
cell_ptr++;
|
||||
}
|
||||
// Remember where to find the current cell
|
||||
current_cell_ptr = cell_ptr + width_in_bytes;
|
||||
current_mask = mask;
|
||||
// Count upper neighbor
|
||||
if ((*cell_ptr & mask)) neighbor_count++;
|
||||
// Count lower neighbor
|
||||
if ((*(cell_ptr + width_in_bytesX2) & mask))
|
||||
neighbor_count++;
|
||||
// Point to upper right neighbor
|
||||
if ((mask >>= 1) == 0) {
|
||||
mask = 0x80;
|
||||
cell_ptr++;
|
||||
}
|
||||
// Count upper right neighbor
|
||||
if ((*cell_ptr & mask)) neighbor_count++;
|
||||
// Count right neighbor
|
||||
if ((*(cell_ptr + width_in_bytes) & mask)) neighbor_count++;
|
||||
// Count lower right neighbor
|
||||
if ((*(cell_ptr + width_in_bytesX2) & mask))
|
||||
neighbor_count++;
|
||||
if (*current_cell_ptr & current_mask) {
|
||||
if ((neighbor_count != 2) && (neighbor_count != 3)) {
|
||||
*(dest_cell_ptr + (current_cell_ptr - cells)) &=
|
||||
~current_mask; // turn off cell
|
||||
draw_pixel(x, y, OFF_COLOR);
|
||||
}
|
||||
} else {
|
||||
if (neighbor_count == 3) {
|
||||
*(dest_cell_ptr + (current_cell_ptr - cells)) |=
|
||||
current_mask; // turn on cell
|
||||
draw_pixel(x, y, ON_COLOR);
|
||||
}
|
||||
}
|
||||
// Advance to the next cell on row
|
||||
if ((base_mask >>= 1) == 0) {
|
||||
base_mask = 0x80;
|
||||
base_cell_ptr++; // advance to the next cell byte
|
||||
}
|
||||
}
|
||||
row_cell_ptr += width_in_bytes; // point to start of next row
|
||||
}
|
||||
}
|
||||
|
||||
Listing 17.4 and Listing 17.3 are functionally the same; the only
|
||||
difference lies in how **next\_generation()** is implemented. (Only
|
||||
**next\_generation()** is shown in Listing 17.4; the program is
|
||||
otherwise identical to Listing 17.3.) Listing 17.4 applies the following
|
||||
optimizations to **next\_generation()**:
|
||||
|
||||
The neighbor-counting code is brought into **next\_generation,**
|
||||
eliminating many function calls and from-scratch address/mask
|
||||
calculations; all multiplies are eliminated by using pointers and
|
||||
addition; and all cells are accessed directly via pointers and masks,
|
||||
eliminating all remaining function calls and from-scratch address/mask
|
||||
calculations.
|
||||
|
||||
The net effect of these optimizations is that Listing 17.4 is more than
|
||||
twice as fast as Listing 17.3; we’ve achieved the desired 18 generations
|
||||
per second, albeit only on a 486, and only at 96x96. (The **\#define**
|
||||
that enables code limiting the speed to 18 Hz, which seemed ridiculous
|
||||
in Listing 17.1, is actually useful for keeping the generations from
|
||||
iterating too quickly when Listing 17.4 is running on a 486, especially
|
||||
with a small cellmap like 48x48.) We’ve sped things up by about eight
|
||||
times so far; we need to increase our speed another ten times to reach
|
||||
our goal of 200x200 at 18 generations per second on a 20 MHz 386.
|
||||
|
||||
It’s undoubtedly possible to improve the performance of Listing 17.4
|
||||
further by fine-tuning the code, but no tremendous improvement is
|
||||
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.*
|
||||
------------------- --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
------------------------ --------------------------------- --------------------
|
||||
[Previous](17-04.html) [Table of Contents](index.html) [Next](17-06.html)
|
||||
------------------------ --------------------------------- --------------------
|
||||
|
||||
* * * * *
|
||||
|
||||
Graphics Programming Black Book © 2001 Michael Abrash
|
||||
Loading…
Reference in a new issue