117 lines
No EOL
8.1 KiB
Markdown
117 lines
No EOL
8.1 KiB
Markdown
------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
 *A line-drawing subroutine, which executes perhaps a dozen instructions for each display memory access, generally loses less performance to the display adapter cycle-eater than does a block-copy or scrolling subroutine that uses **REP MOVS** instructions. Scaled and three-dimensional graphics, which spend a great deal of time performing calculations (often using very slow floating-point arithmetic), tend to suffer less.*
|
|
------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
|
|
In addition, code that accesses display memory infrequently tends to
|
|
suffer only about half of the maximum display memory wait states,
|
|
because on average such code will access display memory halfway between
|
|
one available display memory access slot and the next. As a result, code
|
|
that accesses display memory less intensively than the code in Listing
|
|
4.11 will on average lose 4 or 5 rather than 8-plus cycles to the
|
|
display adapter cycle-eater on each memory access.
|
|
|
|
Nonetheless, the display adapter cycle-eater always takes its toll on
|
|
graphics code. Interestingly, that toll becomes much higher on ATs and
|
|
80386 machines because while those computers can execute many more
|
|
instructions per microsecond than can the 8088-based PC, it takes just
|
|
as long to access display memory on those computers as on the 8088-based
|
|
PC. Remember, the limited speed of access to a graphics adapter is an
|
|
inherent characteristic of the adapter, so the fastest computer around
|
|
can't access display memory one iota faster than the adapter will allow.
|
|
|
|
#### What to Do about the Display Adapter Cycle-Eater? {#Heading22}
|
|
|
|
What can we do about the display adapter cycle-eater? Well, we can
|
|
minimize display memory accesses whenever possible. In particular, we
|
|
can try to avoid read/modify/write display memory operations of the sort
|
|
used to mask individual pixels and clip images. Why? Because
|
|
read/modify/write operations require two display memory accesses (one
|
|
read and one write) each time display memory is manipulated. Instead, we
|
|
should try to use writes of the sort that set all the pixels in a given
|
|
byte of display memory at once, since such writes don't require
|
|
accompanying read accesses. The key here is that only half as many
|
|
display memory accesses are required to write a byte to display memory
|
|
as are required to read a byte from display memory, mask part of it off
|
|
and alter the rest, and write the byte back to display memory. Half as
|
|
many display memory accesses means half as many display memory wait
|
|
states.
|
|
|
|
------------------- --------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
 *Moreover, 486s and Pentiums, as well as recent Super VGAs, employ write-caching schemes that make display memory writes considerably faster than display memory reads.*
|
|
------------------- --------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
|
|
Along the same line, the display adapter cycle-eater makes the popular
|
|
exclusive-OR animation technique, which requires paired reads and writes
|
|
of display memory, less-than-ideal for the PC. Exclusive-OR animation
|
|
should be avoided in favor of simply writing images to display memory
|
|
whenever possible.
|
|
|
|
Another principle for display adapter programming on the 8088 is to
|
|
perform multiple accesses to display memory very rapidly, in order to
|
|
make use of as many of the scarce accesses to display memory as
|
|
possible. This is especially important when many large images need to be
|
|
drawn quickly, since only by using virtually every available display
|
|
memory access can many bytes be written to display memory in a short
|
|
period of time. Repeated string instructions are ideal for making
|
|
maximum use of display memory accesses; of course, repeated string
|
|
instructions can only be used on whole bytes, so this is another point
|
|
in favor of modifying display memory a byte at a time. (On faster
|
|
processors, however, display memory is so slow that it often pays to do
|
|
several instructions worth of work between display memory accesses, to
|
|
take advantage of cycles that would otherwise be wasted on the wait
|
|
states.)
|
|
|
|
It would be handy to explore the display adapter cycle-eater issue in
|
|
depth, with lots of example code and execution timings, but alas, I
|
|
don't have the space for that right now. For the time being, all you
|
|
really need to know about the display adapter cycle-eater is that on the
|
|
8088 you can lose more than 8 cycles of execution time on each access to
|
|
display memory. For intensive access to display memory, the loss really
|
|
can be as high as 8cycles (and up to 50, 100, or even more on 486s and
|
|
Pentiums paired with slow VGAs), while for average graphics code the
|
|
loss is closer to 4 cycles; in either case, the impact on performance is
|
|
significant. There is only one way to discover just how significant the
|
|
impact of the display adapter cycle-eater is for any particular graphics
|
|
code, and that is of course to measure the performance of that code.
|
|
|
|
#### Cycle-Eaters: A Summary {#Heading23}
|
|
|
|
We've covered a great deal of sophisticated material in this chapter, so
|
|
don't feel bad if you haven't understood everything you've read; it will
|
|
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
|
|
to be 4 cycles longer than an equivalent access to a byte-sized
|
|
operand.
|
|
- 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
|
|
reductions ranging as high as 8.33 percent.
|
|
- 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.
|
|
|
|
This basic knowledge about cycle-eaters puts you in a good position to
|
|
understand the results reported by the Zen timer, and that means that
|
|
you're well on your way to writing high-performance assembler code.
|
|
|
|
#### What Does It All Mean? {#Heading24}
|
|
|
|
There you have it: life under the programming interface. It's not a
|
|
particularly pretty picture for the inhabitants of that strange realm
|
|
where hardware and software meet are little-known cycle-eaters that sap
|
|
the speed from your unsuspecting code. Still, some of those cycle-eaters
|
|
can be minimized by keeping instructions short, using the registers,
|
|
using byte-sized memory operands, and accessing display memory as little
|
|
as possible. None of the cycle-eaters can be eliminated, and dynamic RAM
|
|
refresh can scarcely be addressed at all; still, aren't you better off
|
|
knowing how fast your code *really* runs—and why—than you were reading
|
|
the official execution times and guessing? And while specific
|
|
cycle-eaters vary in importance on later x86-family processors, with
|
|
some cycle-eaters vanishing altogether and new ones appearing, the
|
|
concept that understanding these obscure gremlins is a key to
|
|
performance remains unchanged, as we'll see again and again in later
|
|
chapters. |