129 lines
8.6 KiB
Markdown
129 lines
8.6 KiB
Markdown
------------------------ --------------------------------- --------------------
|
||
[Previous](04-09.html) [Table of Contents](index.html) [Next](05-01.html)
|
||
------------------------ --------------------------------- --------------------
|
||
|
||
------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||
 *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.
|
||
|
||
------------------------ --------------------------------- --------------------
|
||
[Previous](04-09.html) [Table of Contents](index.html) [Next](05-01.html)
|
||
------------------------ --------------------------------- --------------------
|
||
|
||
* * * * *
|
||
|
||
Graphics Programming Black Book © 2001 Michael Abrash
|