Use markdown style lists
This commit is contained in:
parent
2091516544
commit
307b59584f
17 changed files with 120 additions and 120 deletions
14
01-02.md
14
01-02.md
|
|
@ -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
|
||||
|
|
|
|||
22
01-04.md
22
01-04.md
|
|
@ -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
|
||||
|
|
|
|||
8
04-01.md
8
04-01.md
|
|
@ -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
|
||||
|
|
|
|||
8
04-10.md
8
04-10.md
|
|
@ -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.
|
||||
|
||||
|
|
|
|||
10
08-02.md
10
08-02.md
|
|
@ -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.
|
||||
|
||||
|
|
|
|||
14
11-03.md
14
11-03.md
|
|
@ -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
|
||||
|
|
|
|||
38
16-01.md
38
16-01.md
|
|
@ -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
|
||||
|
|
|
|||
4
17-01.md
4
17-01.md
|
|
@ -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
|
||||
|
|
|
|||
6
17-06.md
6
17-06.md
|
|
@ -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}
|
||||
|
||||
|
|
|
|||
12
17-08.md
12
17-08.md
|
|
@ -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
|
||||
|
|
|
|||
6
38-02.md
6
38-02.md
|
|
@ -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).
|
||||
|
||||
\
|
||||
**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
|
||||
|
|
|
|||
6
39-01.md
6
39-01.md
|
|
@ -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
|
||||
|
|
|
|||
28
40-02.md
28
40-02.md
|
|
@ -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.
|
||||
|
||||
\
|
||||
**Figure 40.3** *The global and active edge tables as linked lists.*
|
||||
|
|
|
|||
32
45-06.md
32
45-06.md
|
|
@ -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
|
||||
|
|
|
|||
10
52-01.md
10
52-01.md
|
|
@ -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.
|
||||
|
|
|
|||
12
59-06.md
12
59-06.md
|
|
@ -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
|
||||
|
|
|
|||
10
62-03.md
10
62-03.md
|
|
@ -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.
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue