Use markdown style lists

This commit is contained in:
James Gregory 2013-12-31 07:31:51 +11:00
commit 307b59584f
17 changed files with 120 additions and 120 deletions

View file

@ -14,20 +14,20 @@ pages: 007-009
We've got the following rules for creating high-performance software:
- Know where you're going (understand the objective of the software).
- Make a big map (have an overall program design firmly in mind, so
* Know where you're going (understand the objective of the software).
* Make a big map (have an overall program design firmly in mind, so
the various parts of the program and the data structures work well
together).
- Make lots of little maps (design an algorithm for each separate part
* Make lots of little maps (design an algorithm for each separate part
of the overall design).
- Know the territory (understand exactly how the computer carries out
* Know the territory (understand exactly how the computer carries out
each task).
- Know when it matters (identify the portions of your programs where
* Know when it matters (identify the portions of your programs where
performance matters, and don't waste your time optimizing the rest).
- Always consider the alternatives (don't get stuck on a single
* Always consider the alternatives (don't get stuck on a single
approach; odds are there's a better way, if you're clever and
inventive enough).
- Know how to turn on the juice (optimize the code as best you know
* Know how to turn on the juice (optimize the code as best you know
how when it *does* matter).
Making rules is easy; the hard part is figuring out how to apply them in

View file

@ -105,17 +105,17 @@ whizzing through the data in the buffer inside a single loop.
There are four reasons that many programmers would give for not trying
to improve on Listing 1.4:
**1.**  The code is already fast enough.
**2.**  The code works, and some people are content with code that
works, even when it's slow enough to be annoying.
**3.**  The C library is written in optimized assembly, and it's likely
to be faster than any code that the average programmer could write to
perform essentially the same function.
**4.**  The C library conveniently handles the buffering of file data,
and it would be a nuisance to have to implement that capability.
1. The code is already fast enough.
2. The code works, and some people are content with code that
works, even when it's slow enough to be annoying.
3. The C library is written in optimized assembly, and it's likely
to be faster than any code that the average programmer could write to
perform essentially the same function.
4. The C library conveniently handles the buffering of file data,
and it would be a nuisance to have to implement that capability.
I'll ignore the first reason, both because performance is no longer an
issue if the code is fast enough and because the current application

View file

@ -115,10 +115,10 @@ indeed.
The major cycle-eaters are:
- The 8088's 8-bit external data bus.
- The prefetch queue.
- Dynamic RAM refresh.
- Wait states, notably display memory wait states and, in the AT and
* The 8088's 8-bit external data bus.
* The prefetch queue.
* Dynamic RAM refresh.
* Wait states, notably display memory wait states and, in the AT and
80386 computers, system memory wait states.
The locations of these cycle-eaters in the primordial 8088-based PC are

View file

@ -94,15 +94,15 @@ all become clear from further reading, especially once you study, time,
and tune code that you have written yourself. What's really important is
that you come away from this chapter understanding that on the 8088:
- The 8-bit bus cycle-eater causes each access to a word-sized operand
* The 8-bit bus cycle-eater causes each access to a word-sized operand
to be 4 cycles longer than an equivalent access to a byte-sized
operand.
- The prefetch queue cycle-eater can cause instruction execution times
* The prefetch queue cycle-eater can cause instruction execution times
to be as much as four times longer than the officially documented
cycle times.
- The DRAM refresh cycle-eater slows most PC code, with performance
* The DRAM refresh cycle-eater slows most PC code, with performance
reductions ranging as high as 8.33 percent.
- The display adapter cycle-eater typically doubles and can more than
* The display adapter cycle-eater typically doubles and can more than
triple the length of the standard 4-cycle access to display memory,
with intensive display memory access suffering most.

View file

@ -119,14 +119,14 @@ More on this shortly.
To recap, here are some things to look for when striving to convert C
code into optimized assembly language:
- Move the entire performance-critical section into a single assembly
* Move the entire performance-critical section into a single assembly
language function.
- Don't use calls or stack frame accesses inside the critical code, if
* Don't use calls or stack frame accesses inside the critical code, if
possible, and avoid unnecessary memory accesses of any kind.
- Change segments as infrequently as possible.
- Optimize in terms of what assembly does well, *not* in terms of
* Change segments as infrequently as possible.
* Optimize in terms of what assembly does well, *not* in terms of
fine-tuning compiled C code.
- Change the rules to the benefit of assembly, if necessary; for
* Change the rules to the benefit of assembly, if necessary; for
example, reorganize data structto allow efficient assembly language
processing.

View file

@ -29,22 +29,22 @@ outrun even zero—5 cycles longer than the official execution time.)
To summarize:
- Memory-accessing instructions don't run at their official speeds on
* Memory-accessing instructions don't run at their official speeds on
non-zero-wait-state 286/386 computers.
- The prefetch queue cycle-eater reduces performance on 286/386
* The prefetch queue cycle-eater reduces performance on 286/386
computers, particularly when non-zero-wait-state memory is used.
- Branches often execute at less than their rated speeds on the 286
* Branches often execute at less than their rated speeds on the 286
and 386 since the prefetch queue is emptied.
- The extent to which the prefetch queue and wait states affect
* The extent to which the prefetch queue and wait states affect
performance varies from one 286/386 computer to another, making
precise optimization impossible.
What's to be learned from all this? Several things:
- Keep your instructions short.
- Keep it in the registers; avoid memory, since memory generally can't
* Keep your instructions short.
* Keep it in the registers; avoid memory, since memory generally can't
keep up with the processor.
- Don't jump.
* Don't jump.
Of course, those are exactly the rules that apply to 8088 optimization
as well. Isn't it convenient that the same general rules apply across

View file

@ -21,25 +21,25 @@ listing the 10 leading concerns of corporate buyers when it comes to
C++. Boiled down, the list looked like this, in order of descending
importance to buyers:
**1.**  Debugging
**2.**  Documentation
**3.**  Windows development tools
**4.**  High-level Windows support
**5.**  Class library
**6.**  Development cycle efficiency
**7.**  Object-oriented development aids
**8.**  Programming management aids
**9.**  Online help
**10.**  Windows development cycle automation
1. Debugging
2. Documentation
3. Windows development tools
4. High-level Windows support
5. Class library
6. Development cycle efficiency
7. Object-oriented development aids
8. Programming management aids
9. Online help
10. Windows development cycle automation
Is something missing here? You bet your maximum *gluteus* something's
missing—nowhere on that list is there so much as one word about how fast

View file

@ -73,9 +73,9 @@ vertically, and four diagonally. For each succeeding generation of
cells, the game logic determines whether each cell will be on or off
according to the following rules:
- If a cell is on and has either two or three neighbors that are on in
* If a cell is on and has either two or three neighbors that are on in
the current generation, it stays on; otherwise, the cell turns off.
- If a cell is off and has exactly three "on" neighbors in the current
* If a cell is off and has exactly three "on" neighbors in the current
generation, it turns on; otherwise, it stays off. That's all the
rules there are—but they give rise to an astonishing variety of
forms, including patterns that spin, march across the screen, and

View file

@ -17,11 +17,11 @@ Listing 17.4—and it's still in C++.
How is this possible? Here are some hints:
- After a few dozen generations, most of the cellmap consists of cells
* After a few dozen generations, most of the cellmap consists of cells
in the off state.
- There are many possible cellmap representations other than one
* There are many possible cellmap representations other than one
bit-per-pixel.
- Cells change state relatively infrequently.
* Cells change state relatively infrequently.
### Bringing In the Right Brain {#Heading8}

View file

@ -59,20 +59,20 @@ updating the cellmap was what I wanted people to concentrate on.
Here are the rules I laid down for the challenge:
- Readers could modify any code in Listing 17.5, except the main loop,
* Readers could modify any code in Listing 17.5, except the main loop,
as well as change the cell map representation any way they liked.
However, the code had to produce exactly the same output as Listing
17.5 under all circumstances in order to be eligible to win.
- Engine code had to be less than 400 lines long *in total,* excluding
* Engine code had to be less than 400 lines long *in total,* excluding
the video-related code shown in Listing 17.2.
- Submissions had to compile/assemble with Borland C++ (in either C++
* Submissions had to compile/assemble with Borland C++ (in either C++
or C mode, as desired) and/or TASM.
- All submissions had to handle cellmaps at least 200x200 in size.
- Assembly language could of course be used to speed up any part of
* All submissions had to handle cellmaps at least 200x200 in size.
* Assembly language could of course be used to speed up any part of
the program. C rather than C++ was legal as well, so long as entered
implementations produced the same results as Listing 17.5 and 17.2
together and were less than 400 lines long.
- All entries would be timed on the same 33 MHz 486 with a 256K
* All entries would be timed on the same 33 MHz 486 with a 256K
external cache.
That was the challenge I put to the readers. Little did I realize the

View file

@ -21,17 +21,17 @@ to handle points that are exactly on the boundary, and points that lie
at vertices, so that those points are drawn once and only once. To deal
with that, we're going to adopt the following rules:
- Points located exactly on nonhorizontal edges are drawn only if the
* Points located exactly on nonhorizontal edges are drawn only if the
interior of the polygon is directly to the right (left edges are
drawn, right edges aren't).
![](images/38-03.jpg)\
**Figure 38.3**  *The adjacent polygons problem.*
- Points located exactly on horizontal edges are drawn only if the
* Points located exactly on horizontal edges are drawn only if the
interior of the polygon is directly below them (horizontal top edges
are drawn, horizontal bottom edges aren't).
- A vertex is drawn only if all lines ending at that point meet the
* A vertex is drawn only if all lines ending at that point meet the
above conditions (no right or bottom edges end at that point).
All edges of a polygon except those that are flat tops or flat bottoms

View file

@ -105,11 +105,11 @@ polygon-filling code that's fast enough to be truly usable.
Our original polygon filling code involved three major tasks, each
performed by a separate function:
- Tracing each polygon edge to generate a coordinate list (performed
* Tracing each polygon edge to generate a coordinate list (performed
by the function **ScanEdge);**
- Drawing the scanned-out horizontal lines that constitute the filled
* Drawing the scanned-out horizontal lines that constitute the filled
polygon (**DrawHorizontalLineList** ); and
- Characterizing the polygon and coordinating the tracing and drawing
* Characterizing the polygon and coordinating the tracing and drawing
(**FillConvexPolygon** ).
The amount of time that the previous chapter's sample program spent in

View file

@ -41,25 +41,25 @@ Y coordinates, error terms and error term adjustments, lengths, and
directions of X movement for each edge. Once the GET is built, we'll do
the following:
**1.**  Set the current Y coordinate to the Y coordinate of the first
edge in the GET.
1. Set the current Y coordinate to the Y coordinate of the first
edge in the GET.
**2.**  Move all edges with the current Y coordinate from the GET to the
AET, removing them from the GET and maintaining the X-sorted order of
the AET.
2. Move all edges with the current Y coordinate from the GET to the
AET, removing them from the GET and maintaining the X-sorted order of
the AET.
**3.**  Draw all odd-to-even spans in the AET at the current Y
coordinate.
3. Draw all odd-to-even spans in the AET at the current Y
coordinate.
**4.**  Count down the lengths of all edges in the AET, removing any
edges that are done, and advancing the X coordinates of all remaining
edges in the AET by one scan line.
4. Count down the lengths of all edges in the AET, removing any
edges that are done, and advancing the X coordinates of all remaining
edges in the AET by one scan line.
**5.**  Sort the AET in order of ascending X coordinate.
5. Sort the AET in order of ascending X coordinate.
**6.**  Advance the current Y coordinate by one scan line.
**7.**  If either the AET or GET isn't empty, go to step 2.
6. Advance the current Y coordinate by one scan line.
7. If either the AET or GET isn't empty, go to step 2.
![](images/40-03.jpg)\
**Figure 40.3**  *The global and active edge tables as linked lists.*

View file

@ -40,22 +40,22 @@ Serge didn't care to do all that bookkeeping in his animation
applications, so he came up with the following approach, which I've
reworded, amplified, and slightly modified in the summary here:
**1.**  Set the start address to display page 0.
1. Set the start address to display page 0.
**2.**  Draw to page 1.
2. Draw to page 1.
**3.**  Set the start address to display page 1 (the newly drawn page),
then wait for the leading edge of vertical sync, at which point the page
has flipped and it's safe to modify page 0.
3. Set the start address to display page 1 (the newly drawn page),
then wait for the leading edge of vertical sync, at which point the page
has flipped and it's safe to modify page 0.
**4.**  Copy, via the latches, from page 1 to page 0 the areas that
changed from the previous screen to the current one.
4. Copy, via the latches, from page 1 to page 0 the areas that
changed from the previous screen to the current one.
**5.**  Set the start address to display page 0, which is now identical
to page 1, then wait for the leading edge of vertical sync, at which
point the page has flipped and it's safe to modify page 1.
5. Set the start address to display page 0, which is now identical
to page 1, then wait for the leading edge of vertical sync, at which
point the page has flipped and it's safe to modify page 1.
**6.**  Go to step 2.
6. Go to step 2.
The great benefit of Serge's approach is that the only page that is ever
actually drawn to (as opposed to being block-copied to) is page 1. Only
@ -74,14 +74,14 @@ less-experienced animation programmers.
An interesting variation on Serge's approach doesn't page flip nor wait
for vertical sync:
**1.**  Set the start address to display page 0.
1. Set the start address to display page 0.
**2.**  Draw to page 1.
2. Draw to page 1.
**3.**  Copy, via the latches, the areas that changed from the last
screen to the current one from page 1 to page 0.
3. Copy, via the latches, the areas that changed from the last
screen to the current one from page 1 to page 0.
**4.**  Go to step 2.
4. Go to step 2.
This approach totally eliminates page flipping, which can consume a
great deal of time. The downside is that images may shear for one frame

View file

@ -79,12 +79,12 @@ code builds on the code from no fewer than five previous chapters. The
code that's required in order to link this chapter's animation demo
program is the following:
- Listing 50.1 from Chapter 50 (draw clipped line list);
- Listings 47.1 and 47.6 from Chapter 47 (Mode X mode set, rectangle
* Listing 50.1 from Chapter 50 (draw clipped line list);
* Listings 47.1 and 47.6 from Chapter 47 (Mode X mode set, rectangle
fill);
- Listing 49.6 from Chapter 49;
- Listing 39.4 from Chapter 39 (polygon edge scan); and
- The **FillConvexPolygon( )** function from Listing 38.1 from Chapter
* Listing 49.6 from Chapter 49;
* Listing 39.4 from Chapter 39 (polygon edge scan); and
* The **FillConvexPolygon( )** function from Listing 38.1 from Chapter
38. Note that the **struct** keywords in **FillConvexPolygon( )**
must be removed to reflect the switch to typedefs in the animation
header file.

View file

@ -40,17 +40,17 @@ measurably faster than Listing 59.2, but not even close to being
The moral of this story (apart from it being a good idea to enable
compiler optimization) is:
**1.**  Understand what you're doing, through and through.
1. Understand what you're doing, through and through.
**2.**  Build a complete and consistent model in your head.
2. Build a complete and consistent model in your head.
**3.**  Design from the principles that the model provides.
3. Design from the principles that the model provides.
**4.**  Implement the design.
4. Implement the design.
**5.**  Measure to learn what you've wrought.
5. Measure to learn what you've wrought.
**6.**  Go back to step 1 and apply what you've just learned.
6. Go back to step 1 and apply what you've just learned.
With each iteration you'll dig deeper, learn more, and improve your
ability to know where and how to focus your design and programming

View file

@ -16,12 +16,12 @@ Conceptually rendering from a BSP tree really is that simple, but the
implementation is a bit more complicated. The full rendering pipeline,
as coordinated by **UpdateWorld()**, is this:
- Update the current location.
- Transform all wall endpoints into viewspace (the world as seen from
* Update the current location.
* Transform all wall endpoints into viewspace (the world as seen from
the current location with the current viewing angle).
- Clip all walls to the view pyramid.
- Project wall vertices to screen coordinates.
- Walk the walls back to front, and for each wall that lies at least
* Clip all walls to the view pyramid.
* Project wall vertices to screen coordinates.
* Walk the walls back to front, and for each wall that lies at least
partially in the view pyramid, perform backface culling (skip walls
facing away from the viewer), and draw the wall if it's not culled.