|Previous||Table of Contents||Next|
when declaring the timer routines extern, so that name-mangling doesnt occur, and the linker can find the routines C-style names.)
Thats all it takes; after doing this, youll 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();
(Im talking about the precision timer here. The long-period timerListing 3.5requires 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 Cs 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.
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
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, its 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 codeincluding the far calls to ZTimerOn and ZTimerOff! 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.
Whats 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, its 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 TASMs alteration of far calls only because I happened to view the code in the debugger, and you might want to do the same if youre using a recent version of MASM.
Ive 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.
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 Intels 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 PCs hardware and BIOS resources, I suggest you consult IBMs 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, its 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 weve accomplished that in this chapter.
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 its a tool well use frequently for the remainder of this book.
|Previous||Table of Contents||Next|