123 lines
No EOL
5.7 KiB
Markdown
123 lines
No EOL
5.7 KiB
Markdown
when declaring the timer routines **extern**, so that name-mangling
|
||
doesn't occur, and the linker can find the routines' C-style names.)
|
||
|
||
That's all it takes; after doing this, you'll be able to use the Zen
|
||
timer from C, as, for example, in:
|
||
|
||
ZTimerOn():
|
||
for (i=0, x=0; i<100; i++)
|
||
x += i;
|
||
ZTimerOff();
|
||
ZTimerReport();
|
||
|
||
(I'm talking about the precision timer here. The long-period
|
||
timer—Listing 3.5—requires the same modifications, but to different
|
||
lines.)
|
||
|
||
\
|
||
**Figure 3.2** *Changes for use with small code model C.*
|
||
|
||
Altering the Zen timer for use in C's large code model is a tad more
|
||
complex, because in addition to the above changes, all functions,
|
||
including the internal reference timing routines that are used to
|
||
calculate overhead so it can be subtracted out, must be converted to
|
||
far. Figure 3.3 shows the line numbers and new states of all lines from
|
||
Listing 3.1 that must be changed in order to call the Zen timer from
|
||
large code model C. Again, the line numbers are specific to the
|
||
precision timer, but the long-period timer is very similar.
|
||
|
||
The full listings for the C-callable Zen timers are presented in Chapter
|
||
K on the companion CD-ROM.
|
||
|
||
#### Watch Out for Optimizing Assemblers! {#Heading16}
|
||
|
||
One important safety tip when modifying the Zen timer for use with large
|
||
code model C code: Watch out for optimizing assemblers! TASM actually
|
||
replaces
|
||
|
||
call far ptr ReferenceZTimerOn
|
||
|
||
with
|
||
|
||
push cs
|
||
call near ptr ReferenceZTimerOn
|
||
|
||
(and likewise for **ReferenceZTimerOff** ), which works because
|
||
**ReferenceZTimerOn** is in the same segment as the calling code. This
|
||
is normally a great optimization, being both smaller and faster than a
|
||
far call. However, it's not so great for the Zen
|
||
|
||
\
|
||
**Figure 3.3** *Changes for use with large code model C.*
|
||
|
||
timer, because our purpose in calling the reference timing code is to
|
||
determine exactly how much time is taken by overhead code—including the
|
||
far calls to **ZTimerOn** and **ZTimerOf**f! By converting the far calls
|
||
to push/near call pairs within the Zen timer module, TASM makes it
|
||
impossible to emulate exactly the overhead of the Zen timer, and makes
|
||
timings slightly (about 16 cycles on a 386) less accurate.
|
||
|
||
What's the solution? Put the **NOSMART** directive at the start of the
|
||
Zen timer code. This directive instructs TASM to turn off all
|
||
optimizations, including converting far calls to push/near call pairs.
|
||
By the way, there is, to the best of my knowledge, no such problem with
|
||
MASM up through version 5.10A.
|
||
|
||
In my mind, the whole business of optimizing assemblers is a mixed
|
||
blessing. In general, it's nice to have the assembler shortening jumps
|
||
and selecting sign-extended forms of instructions for you. On the other
|
||
hand, the benefits of tricks like substituting push/near call pairs for
|
||
far calls are relatively small, and those tricks can get in the way when
|
||
complete control is needed. Sure, complete control is needed very
|
||
rarely, but when it is, optimizing assemblers can cause subtle problems;
|
||
I discovered TASM's alteration of far calls only because I happened to
|
||
view the code in the debugger, and you might want to do the same if
|
||
you're using a recent version of MASM.
|
||
|
||
I've tested the changes shown in Figures 3.2 and 3.3 with TASM and
|
||
Borland C++ 4.0, and also with the latest MASM and Microsoft C/C++
|
||
compiler.
|
||
|
||
#### Further Reading {#Heading17}
|
||
|
||
For those of you who wish to pursue the mechanics of code measurement
|
||
further, one good article about measuring code performance with the 8253
|
||
timer is "Programming Insight: High-Performance Software Analysis on the
|
||
IBM PC," by Byron Sheppard, which appeared in the January, 1987 issue of
|
||
*Byte*. For complete if somewhat cryptic information on the 8253 timer
|
||
itself, I refer you to Intel's *Microsystem Components Handbook*, which
|
||
is also a useful reference for a number of other PC components,
|
||
including the 8259 Programmable Interrupt Controller and the 8237 DMA
|
||
Controller. For details about the way the 8253 is used in the PC, as
|
||
well as a great deal of additional information about the PC's hardware
|
||
and BIOS resources, I suggest you consult IBM's series of technical
|
||
reference manuals for the PC, XT, AT, Model 30, and microchannel
|
||
computers, such as the Models 50, 60, and 80.
|
||
|
||
For our purposes, however, it's not critical that you understand exactly
|
||
how the Zen timer works. All you really need to know is what the Zen
|
||
timer can do and how to use it, and we've accomplished that in this
|
||
chapter.
|
||
|
||
#### Armed with the Zen Timer, Onward and Upward {#Heading18}
|
||
|
||
The Zen timer is not perfect. For one thing, the finest resolution to
|
||
which it can measure an interval is at best about 1µs, a period of time
|
||
in which a 66 MHz Pentium computer can execute as many as 132
|
||
instructions (although an 8088-based PC would be hard-pressed to manage
|
||
two instructions in a microsecond). Another problem is that the timing
|
||
code itself interferes with the state of the prefetch queue and
|
||
processor cache at the start of the code being timed, because the timing
|
||
code is not necessarily fetched and does not necessarily access memory
|
||
in exactly the same time sequence as the code immediately preceding the
|
||
code under measurement normally does. This prefetch effect can introduce
|
||
as much as 3 to 4 µ of inaccuracy. Similarly, the state of the prefetch
|
||
queue at the end of the code being timed affects how long the code that
|
||
stops the timer takes to execute. Consequently, the Zen timer tends to
|
||
be more accurate for longer code sequences, since the relative magnitude
|
||
of the inaccuracy introduced by the Zen timer becomes less over longer
|
||
periods.
|
||
|
||
Imperfections notwithstanding, the Zen timer is a good tool for
|
||
exploring C code and x86 family assembly language, and it's a tool we'll
|
||
use frequently for the remainder of this book. |