https://wiki.preterhuman.net/index.php?title=Timing_on_the_Macintosh&feed=atom&action=historyTiming on the Macintosh - Revision history2024-03-29T15:24:32ZRevision history for this page on the wikiMediaWiki 1.35.0https://wiki.preterhuman.net/index.php?title=Timing_on_the_Macintosh&diff=17907&oldid=prevNetfreak: Created page with "by Martin Minow <p> <i> The Macintosh offers a rich and flexible set of timing operations that allow you to measure elapsed time, record the time an event occurs, and schedule..."2020-09-03T22:53:45Z<p>Created page with "by Martin Minow <p> <i> The Macintosh offers a rich and flexible set of timing operations that allow you to measure elapsed time, record the time an event occurs, and schedule..."</p>
<p><b>New page</b></p><div>by Martin Minow<br />
<p><br />
<i><br />
The Macintosh offers a rich and flexible set of timing operations that allow you to<br />
measure elapsed time, record the time an event occurs, and schedule actions for<br />
future times. This article pulls together all the available timing options,<br />
including the extended Time Manager and Microseconds routine added with System<br />
7 and new routines that are available with the PCI-based Macintosh.</i><br />
<p><br />
You've probably heard the expression, "Time is nature's way of keeping<br />
everything from happening at once." Well, keeping things from happening at the<br />
same time is especially important on computers, and they're particularly good<br />
at keeping close track of time -- both "clock" time and relative time. This<br />
article shows you how to take advantage of the timing options provided on the<br />
Macintosh, including new routines that are available on the PCI-based<br />
Macintosh.<br />
<p> <br />
There are three common situations in which applications need to keep track<br />
of time:<br />
<ul><br />
<li>measuring elapsed time -- for example, for performance analysis or to<br />
see how long it takes the user or some other external entity to perform an<br />
action <br />
<p><li> recording an event -- for example, to time-stamp a record in a database<br />
or to inform the user when an action occurs <br />
<p><li> scheduling an event -- for example, to start or complete a<br />
time-dependent task</ul><br />
<br />
Several timing-related routines are available on the<br />
Macintosh, and each is useful in certain situations. In general, you should:<br />
<ul><br />
<li> Use GetDateTime (or GetTime) if you need to maintain information across<br />
system restarts or need to relate an event to the calendar.<br />
<p><li> Use TickCount if you need only a relatively crude measure of time or<br />
need to run under System 6.<br />
<p><li> Use the Time Manager's Microseconds routine or Time Manager tasks if<br />
you need improved precision or some attention to drift-free timing. Because the<br />
Time Manager is part of all versions of System 7, it provides the best service<br />
to most clients.<br />
<p><li> Use UpTime if you want the highest-precision timing available and run<br />
only on PCI-based Macintosh systems.</ul>This article presents the basics of<br />
some standard approaches to the three types of timing, along with code examples<br />
using many of the timing tools at your disposal. There's also a discussion of<br />
factors that can affect the precision of your timing operations. A simple<br />
example of using Microseconds accompanies this article on this issue's CD and<br />
<i>develop</i>'s Web site.<br />
<br />
<h3><br />
MEASURING ELAPSED TIME<br />
</h3><br />
The Macintosh provides several functions that can be used to measure elapsed<br />
time. Your choice of routine depends on the degree of precision you require and<br />
the system software you're running under.<br />
<p><br />
The GetDateTime function returns the current clock time as the number of<br />
seconds since January 1, 1904, and the GetTime function returns the clock time<br />
in year, month, day, hour, minute, and second format (in a date-time record).<br />
With their one-second resolution, however, these functions aren't well suited<br />
to measuring elapsed code performance or the duration of user actions. <br />
<ul><br />
<b>January 1, 1904, was chosen</b> as the base for the Macintosh clock because it was<br />
the first leap year of the twentieth century. 1900 wasn't a leap year because<br />
leap years are skipped every 100 years for three centuries. On the fourth<br />
century, which will be the year 2000, the leap year isn't skipped. This means<br />
that by starting with 1904, Macintosh system programmers could save a half<br />
dozen instructions in their leap-year checking code, which they thought was way<br />
cool.</ul><br />
<br />
One of the functions available for finer timing resolution is TickCount, which<br />
returns the time elapsed since the system last started up in units of about<br />
1/60 second. Until System 7, this was the only reasonable way to measure<br />
sub-second intervals. With System 7, the Microseconds routine became available.<br />
(Using the extended Time Manager is another possible method on System 7, but<br />
it's more complicated and so isn't commonly used for that purpose.)<br />
Furthermore, the PCI-based Macintosh provides UpTime, a replacement for<br />
Microseconds.<br />
<h4><br />
THE MICROSECONDS ROUTINE<br />
</h4><br />
The Microseconds routine returns the number of microseconds that have elapsed<br />
since system startup as an unsigned 64-bit integer and offers a convenient way<br />
of timing events and operations. Theoretically, it can resolve intervals of<br />
about 20 microseconds, although in practice it can't time intervals that small<br />
(for reasons given later, in the section on timing accuracy).<br />
<p><br />
The value returned by Microseconds has the UnsignedWide structure, shown in<br />
Listing 1. A signed wide structure is used for the result of subtracting two<br />
Microseconds values to calculate elapsed time. UnsignedWide is defined in<br />
Types.h of the universal headers, but is also shown in Listing 1 for<br />
convenience.<br />
<p><br />
<hr><br />
<p><br />
<b>Listing 1.</b> Microseconds structures<br />
<pre>struct UnsignedWide {<br />
unsigned long hi;<br />
unsigned long lo;<br />
};<br />
typedef struct UnsignedWide UnsignedWide;<br />
<br />
struct wide {<br />
signed long hi;<br />
unsigned long lo;<br />
};<br />
typedef struct wide wide;<br />
<br />
/*<br />
* The sample code defines a SignedWide structure for consistency.<br />
*/<br />
typedef wide SignedWide;<br />
</pre><br />
<hr><br />
<p><br />
To time a routine, your application would do the following:<br />
<br />
<pre>UnsignedWide startTime;<br />
UnsignedWide endTime;<br />
<br />
Microseconds(&amp;startTime);<br />
DoMyOperation();<br />
Microseconds(&amp;endTime);<br />
</pre><br />
<br />
Subtracting startTime from endTime will yield the elapsed time. However, the 64-bit<br />
Microseconds values are rather unwieldy to deal with. The simplest solution is<br />
to convert them to double-precision floating-point numbers.<br />
<p><br />
MicrosecondToDouble, shown in Listing 2, converts a Microseconds value to<br />
double-precision floating point. Using double precision will retain accuracy<br />
for all practical purposes. You can also use integer subtraction to get the<br />
difference between the two times and convert the result to floating point (or<br />
whatever you need) afterward. MicrosecondDelta, also in Listing 2, computes the<br />
difference between two Microseconds result values, returning a signed 64-bit<br />
integer to retain precision. <p><br />
<hr><br />
<p><br />
<b>Listing 2.</b> Microseconds routine support functions<br />
<br />
<pre>#define kTwoPower32 (4294967296.0) /* 2^32 */<br />
<br />
double MicrosecondToDouble(register const UnsignedWide *epochPtr)<br />
{<br />
register double result;<br />
<br />
result = (((double) epochPtr-&gt;hi) * kTwoPower32) + epochPtr-&gt;lo;<br />
return (result);<br />
}<br />
<br />
void MicrosecondDelta(register const UnsignedWide *startPtr,<br />
register const UnsignedWide *endPtr,<br />
register SignedWide *resultPtr)<br />
{<br />
if (endPtr-&gt;lo &gt;= startPtr-&gt;lo)<br />
resultPtr-&gt;hi = endPtr-&gt;hi - startPtr-&gt;hi;<br />
else <br />
resultPtr-&gt;hi = (endPtr-&gt;hi - 1) - startPtr-&gt;hi;<br />
<br />
resultPtr-&gt;lo = endPtr-&gt;lo - startPtr-&gt;lo;<br />
}<br />
</pre><br />
<hr><br />
<p><br />
<ul><br />
<b>If you prefer using only integer arithmetic</b>, the sample code accompanying this<br />
article includes a very simple -- and very inefficient -- 64-bit integer<br />
library with add, subtract, multiply, and divide functions that can be used to<br />
calculate time values. For a more complete 64-bit integer math library, see the<br />
article "64-Bit Integer Math on 680x0 Machines" in <i>develop</i> Issue<br />
26.*</ul><br />
<h4><br />
THE UPTIME ROUTINE<br />
</h4><br />
PCI-based Macintosh systems provide a new routine, UpTime, that returns the<br />
value of the PowerPC internal clock. The value that's returned has the data<br />
type AbsoluteTime and cannot be interpreted directly by applications, because<br />
the units are system dependent and not defined by the API. A library is<br />
provided to convert values of type AbsoluteTime into formats whose units are<br />
known. This approach allows the system to maximize precision and performance. <br />
The time values returned by UpTime start at 0 at system startup and increase<br />
monotonically for as long as it's running. To time a routine with UpTime, your<br />
application might do the following:<br />
<br />
<pre>AbsoluteTime startTime;<br />
AbsoluteTime endTime;<br />
AbsoluteTime elapsedTime;<br />
Nanoseconds elapsedNanoseconds; /* This is an UnsignedWide integer */<br />
<br />
startTime = UpTime();<br />
DoMyOperation();<br />
endTime = UpTime();<br />
elapsedTime = SubAbsoluteFromAbsolute(endTime, startTime);<br />
elapsedNanoseconds = AbsoluteToNanoseconds(elapsedTime);<br />
</pre><br />
<br />
These functions and others used to process AbsoluteTime values are described in<br />
<i>Designing PCI Cards and Drivers for Power Macintosh Computers</i>. <br />
<br />
<h3><br />
RECORDING EVENT OCCURRENCE<br />
</h3><br />
If you need to record when an event occurred (for example, when a record was<br />
added to a database), you can use the value returned by GetDateTime or GetTime.<br />
In most situations, GetDateTime is easier to deal with, being more compact and<br />
saving you from converting days, months, years, and so on into seconds for<br />
computations. <br />
<p><br />
Keep in mind that GetDateTime returns the local clock time, which means that<br />
you can't always use its value to determine which of two records is earlier, as<br />
they could have been created in different time zones or under different<br />
daylight saving time rules. If being able to compare times across time zones is<br />
important, your application should call the ReadLocation routine and store its<br />
MachineLocation result at the time you record the event so that the application<br />
can compute a location-independent value by converting the local time to GMT<br />
(Greenwich Mean Time).<br />
<p><br />
Unfortunately, the local time value returned by GetDateTime isn't coordinated<br />
with the more precise values returned by Microseconds and UpTime. This makes it<br />
difficult to record local times with fractional second resolution. Listing 3<br />
shows one way to work around this problem. It's adapted from the<br />
LogConvertTimestamp function in my PCI device driver sample library, which was<br />
first published in <i>develop</i> Issue 22 ("Creating PCI Device Drivers").<br />
Listing 3 also illustrates my simple 64-bit support library.<br />
<p><br />
<hr><br />
<p><br />
<b>Listing 3.</b> Time of day with fractional second resolution<br />
<pre>void LogConvertTimestamp(<br />
AbsoluteTime eventTime, /* Value to convert */<br />
DateTimeRec *eventDateTime, /* Result goes here */<br />
UInt32 *residualNanoseconds /* Fractional second */<br />
)<br />
{<br />
Nanoseconds eventNanoseconds;<br />
UnsignedWide eventSeconds, temp;<br />
static const UnsignedWide kTenE9 = { 0, 1000000000L };<br />
static UInt32 gUpTimeNumerator;<br />
static UnsignedWide gUpTimeDenominator;<br />
static Nanoseconds gNanosecondsAtStart = { 0, 0 };<br />
<br />
/*<br />
* If this is the first call, compute the offset between<br />
* GetDateTime and UpTime.<br />
*/<br />
if (gNanosecondsAtStart.lo == 0 &amp;&amp; gNanosecondsAtStart.hi == 0) {<br />
UnsignedWide secondsAtStart;<br />
AbsoluteTime absoluteTimeAtStart;<br />
Nanoseconds upTimeAtStart, nanosecondsAtStart;<br />
<br />
secondsAtStart.hi = 0;<br />
GetDateTime(&amp;secondsAtStart.lo);<br />
upTimeAtStart = AbsoluteToNanoseconds(UpTime());<br />
Multiply64(&amp;secondsAtStart, kTenE9.lo, &amp;nanosecondsAtStart);<br />
Subtract64(&amp;nanosecondsAtStart, &amp;upTimeAtStart,<br />
&amp;gNanosecondsAtStart);<br />
}<br />
/*<br />
<br />
* Convert the event time (UpTime value) to nanoseconds and add<br />
* the local time epoch.<br />
*/<br />
eventNanoseconds = AbsoluteToNanoseconds(eventTime);<br />
Add64(&amp;gNanosecondsAtStart, &amp;eventNanoseconds, &amp;eventNanoseconds);<br />
/*<br />
* eventSeconds = eventNanoseconds /= 10e9;<br />
* residualNanoseconds = eventNanoseconds % 10e9;<br />
* Finally, compute the local time (seconds) and fraction.<br />
*/<br />
Divide64(&amp;eventNanoseconds, &amp;kTenE9, &amp;eventSeconds);<br />
*residualNanoseconds = eventNanoseconds.lo;<br />
SecondsToDate(eventSeconds.lo, eventDateTime);<br />
}<br />
</pre><br />
<hr><br />
<ul><br />
<h3><br />
APPLICATION COMPATIBILITY<br />
</h3><br />
Macintosh applications need to check whether particular operating system<br />
functions are available before using them. For example (as you'll see in<br />
Listing 5), the Gestalt function can be used to check for the presence of the<br />
extended Time Manager and the Process Manager. This technique lets your<br />
application configure itself to your customer's exact hardware and system.<br />
<p><br />
If you want to add UpTime support to an application that must also run on<br />
Macintosh systems that lack this function, you'll have to use a different<br />
approach, because your PowerPC application uses the Code Fragment Manager to<br />
link to the shared library that provides this service. If the shared library is<br />
not present on the customer system, your application will not launch (and the<br />
user will be quite perplexed). The simplest way to work around this problem is<br />
to use your development environment's "weak link" or "soft import" capability.<br />
By weak-linking these functions, your application will start even if the<br />
necessary shared library isn't present. This technique is described in detail<br />
in <i>Inside Macintosh: PowerPC System Software</i>, page 1-25.<p><br />
<hr><br />
</ul><br />
<h3><br />
SCHEDULING FUTURE ACTIONS<br />
</h3><br />
Actions can be scheduled at a specific time -- such as 3 P.M. -- or at a<br />
relative time -- like "15 minutes from now." Here we'll look at how an<br />
application can schedule an action for a specific time by polling from its<br />
event loop and how to use the extended Time Manager to initiate an action after<br />
a specific amount of time passes.<br />
<br />
<h4><br />
POLLING FROM THE EVENT LOOP<br />
</h4><br />
The simplest way for an application to schedule an action for a specific time<br />
is to call GetDateTime every so often from the event loop and compare the<br />
returned value with the time set for the scheduled event. (If all your<br />
application is doing is polling for the right time to arrive, be nice and set<br />
the WaitNextEvent sleep time to something long -- 15 seconds, perhaps.) When<br />
the set time matches (or is earlier than) the returned value, the event occurs.<br />
Of course, you can use this solution only if your program is an application<br />
(normal or faceless-background). Code resources should use an extended Time<br />
Manager task with a completion routine instead (as described in the next<br />
section).<br />
<p><br />
The event-loop approach works best when you want to schedule an action for a<br />
specific time because the specified time will be observed even if the user<br />
changes the system's date or time. (Note that under this approach, an event<br />
that just occurred could occur again if the user changes the time backwards.)<br />
However, if it's important that the action happen at some relative amount of<br />
time in the future, you're better off polling with TickCount, Microseconds, or<br />
UpTime or using an extended Time Manager task with a completion routine.<br />
<br />
<h4><br />
USING THE EXTENDED TIME MANAGER<br />
</h4><br />
The extended Time Manager was introduced in System 7 as a way to schedule<br />
accurate periodic actions. Precise timing and real-time synchronization was<br />
becoming more important with increasing use of sound and multimedia. In<br />
addition, the extended Time Manager is the preferred way to schedule an action<br />
for a code resource. Scheduling an action with the extended Time Manager is<br />
suitable for waits of moderate duration (up to a day or so).<br />
<p><br />
The following example uses the extended Time Manager to awaken a process 30<br />
seconds after the timer is started. As shown in Listing 4, the first step is to<br />
define an extended Time Manager task record that includes the timer task, the<br />
process serial number of the process to awaken when the timer expires, and (on<br />
680x0 systems) a pointer to the application's globals. (Throughout this example<br />
we assume an application context, so this value is A5; for THINK and<br />
CodeWarrior nonapplication code, it should be A4 instead.) Listing 4 also<br />
defines the interface for the Time Manager completion routine -- notice that it<br />
varies for 680x0 and PowerPC compilations.<p><br />
<hr><br />
<p><br />
<b>Listing 4.</b> Extended Time Manager definitions<br />
<pre>#include &lt;Types.h&gt;<br />
#include &lt;Timer.h&gt;<br />
#include &lt;OSUtils.h&gt;<br />
#include &lt;GestaltEqu.h&gt;<br />
#include &lt;Processes.h&gt;<br />
<br />
/* Define an extended task record. */<br />
struct ExtendedTimerRec {<br />
TMTask tmTask;<br />
ProcessSerialNumber taskPSN;<br />
#if GENERATINGPOWERPC<br />
/* Nothing needed for PowerPC */<br />
#else<br />
long applicationA5;<br />
#endif<br />
};<br />
typedef struct ExtendedTimerRec ExtendedTimerRec, *ExtendedTimerPtr;<br />
<br />
/* Define the interface for a completion function. */<br />
#if GENERATINGPOWERPC<br />
pascal void TimerCallbackProc(TMTaskPtr tmTaskPtr);<br />
#else /* 680x0 */<br />
pascal void TimerCallbackProc(void);<br />
/*<br />
* This inline function returns the extended Time Manager task <br />
* pointer, which is passed to the completion routine in register A1.<br />
*/<br />
pascal TMTaskPtr GetTMTaskPtr(void) = 0x2E89;<br />
#endif<br />
</pre><br />
<hr><br />
<p><br />
Before you can use your extended task record, you need to be sure the extended Time<br />
Manager and Process Manager are present on the system. Listing 5 shows code<br />
that checks for their presence. If they're present, the code initializes the<br />
extended task record (gExtendedTimerRec), installs the task in the extended<br />
Time Manager's event queue, and starts the timer.<p><br />
<br />
<hr><br />
<p><br />
<b>Listing 5.</b> Starting the timer <br />
<pre>long gestaltResponse;<br />
<br />
if (Gestalt(gestaltTimeMgrVersion, &amp;gestaltResponse) != noErr<br />
|| (gestaltResponse &lt; gestaltExtendedTimeMgr))<br />
goto failure; /* The extended Time Manager is not present. */<br />
if (Gestalt(gestaltOSAttr, &amp;gestaltResponse) != noErr<br />
|| (gestaltResponse &amp; (1L &lt;&lt; gestaltLaunchControl)) == 0)<br />
goto failure; /* The Process Manager is not present. */<br />
/*<br />
* Configure the global structure that stores the timing information.<br />
*/<br />
gExtendedTimerRec.tmTask.qLink = NULL;<br />
gExtendedTimerRec.tmTask.qType = 0;<br />
gExtendedTimerRec.tmTask.tmAddr = NewTimerProc(TimerCallbackProc);<br />
gExtendedTimerRec.tmTask.tmCount = 0;<br />
gExtendedTimerRec.tmTask.tmWakeup = 0;<br />
gExtendedTimerRec.tmTask.tmReserved = 0;<br />
#if GENERATINGPOWERPC<br />
/* Nothing needed for PowerPC. */<br />
#else<br />
gExtendedTimerRec.applicationA5 = SetCurrentA5();<br />
#endif<br />
GetCurrentProcess(&amp;gExtendedTimerRec.taskPSN);<br />
InsXTime((QElemPtr) &amp;gExtendedTimerRec.tmTask);<br />
/*<br />
* Start the timer -- 30-second stall.<br />
*/<br />
PrimeTime((QElemPtr) &amp;gExtendedTimerRec.tmTask, 30000L);<br />
</pre><br />
<hr><br />
<p><br />
You also need to define the extended Time Manager completion routine that's called<br />
when the timer expires (see Listing 6).<p><br />
<hr><br />
<p><br />
<b>Listing 6.</b> Extended Time Manager completion routine<br />
<pre>/* * Define an extended Time Manager completion routine that awakens the <br />
* specified application.<br />
*/<br />
#if GENERATINGPOWERPC<br />
pascal void TimerCallbackProc(TMTaskPtr tmTaskPtr)<br />
{<br />
#else<br />
pascal void TimerCallbackProc(void)<br />
{<br />
TMTaskPtr tmTaskPtr;<br />
long oldA5;<br />
<br />
tmTaskPtr = GetTMTaskPtr();<br />
oldA5 = SetA5(((ExtendedTimerPtr) tmTaskPtr)-&gt;applicationA5);<br />
#endif<br />
gTimerFired = TRUE;<br />
WakeUpProcess(&amp;((ExtendedTimerPtr) tmTaskPtr)-&gt;taskPSN);<br />
#if GENERATINGPOWERPC<br />
/* Nothing needed at completion routine exit. */<br />
#else<br />
SetA5(oldA5);<br />
#endif<br />
}<br />
</pre><br />
<hr><br />
<p><br />
The completion routine is a one-shot timer that awakens the process and exits. You<br />
can easily extend this to perform a periodic wake-up action. Again, note the<br />
use of A5 (use A4 for nonapplication code written in THINK or CodeWarrior).<p><br />
<ul><br />
<hr><br />
<h3>SOME JAVA TIMING TRICKS<br />
</h3><br />
No doubt you already know that with Java, a new version of C++ developed by Sun<br />
Microsystems, you can develop applications that will run on many platforms. If<br />
you're already writing in Java, you might be interested in these useful Java<br />
techniques for dealing with timing issues.<br />
<p><br />
The code below shows how you can synchronize a thread with the current time in<br />
Java. This example was taken from an applet (a small application that's<br />
typically run by a network browser); the entire applet accompanies this<br />
article.<br />
<br />
<pre>/* <br />
* The run method manages an independent <br />
* execution thread. This method runs once a<br />
* minute on the minute.<br />
*/<br />
public void run()<br />
{<br />
Thread thisThread = Thread.currentThread();<br />
thisThread.setPriority(Thread.MIN_PRIORITY);<br />
while (myAppletThread == thisThread) {<br />
/* This does all the work... */<br />
doMyOnceAMinuteProcess();<br />
try { /* Sleep until the next minute. */<br />
long sleepTime = 60000 -<br />
(System.currentTimeMillis() % 60000);<br />
Thread.sleep(sleepTime);<br />
}<br />
catch (InterruptedException e) {} <br />
} /* Loop forever (we just woke up). */ <br />
}<br />
</pre><br />
<br />
Also accompanying this article is a complete class, named Timestamp, that you can<br />
use to measure the amount of time your Java code requires. I used it to analyze<br />
an encryption algorithm:<br />
<p><br />
<pre>int trials = 100;<br />
Timestamp timestamp = new Timestamp();<br />
<br />
for (int i = 0; i &lt; trials; i++) {<br />
timestamp.start();<br />
encryptedData = <br />
encryptor.encode(originalData);<br />
decryptedData = <br />
encryptor.decode(encryptedData);<br />
timestamp.stop();<br />
}<br />
System.out.println(timestamp);<br />
</pre><br />
<br />
System.out.println is a standard Java utility to print a line of text to a log window. Note that<br />
it references the timestamp object. Java interprets this as a reference to the<br />
toString method. That is, System.out.println(timestamp) is identical to<br />
System.out.println(timestamp.toString()). Debugging will be much easier if all<br />
your classes have a toString method that formats their internal state.<p><br />
<hr><br />
</ul><br />
<br />
<h3>TIMING ACCURACY, PRECISION, AND OVERHEAD</h3><br />
If you're measuring performance, remember that you can't trust a single<br />
measurement, as it can be affected by a number of system-related asynchronous<br />
events that you can't always control. Instead, you should take a number of<br />
samples and use a statistical package to understand the variation that may<br />
affect the accuracy and precision of your timing measurement.<br />
<p><br />
On current Macintosh systems, the Microseconds routine uses the hardware VIA<br />
timer as a basis for its calculation. This decrements at a rate of 783360 Hz<br />
and, consequently, limits resolution to about 1.28 microseconds. (Of course,<br />
the mechanism and resolution may change on future systems.) Due to<br />
implementation limitations, however, the Microseconds routine can't time<br />
intervals shorter than about 20 microseconds. If you're using Microseconds to<br />
time a very short interval (such as the execution time of a small code<br />
segment), your analysis may need to adjust the measurements to take into<br />
account the computational overhead of the Microseconds routine itself. This<br />
varies from machine to machine -- and depends, in part, on the influence of<br />
other systemwide processes. An informal measurement of one machine showed that<br />
the following sequence could take as little as zero time up to several hundred<br />
milliseconds:<br />
<br />
<pre>Microseconds(&amp;startTime);<br />
Microseconds(&amp;endTime);<br />
</pre><br />
The reason for this dispersion is that the internal timer is updated as a result of<br />
system interrupts, such as VIA timer and extended Time Manager task completion.<br />
Also, other asynchronous operations on the Macintosh, such as mouse-movement<br />
handlers, file sharing, I/O completion, virtual memory page faults, and network<br />
operations, will interrupt applications. Thus, if you're using Microseconds to<br />
time application program execution, it should be part of a more extensive<br />
statistical data analysis, since any single measurement may result in incorrect<br />
data. As a rule of thumb, to minimize the overhead of calling the routine<br />
itself, the smallest measurement interval should be on the order of one<br />
millisecond.<br />
<p><br />
A similar warning needs to be given regarding the long-term accuracy of the<br />
Microseconds routine: The crystal oscillator used to generate the underlying<br />
time base varies slightly, depending primarily on the ambient temperature.<br />
Here, too, you should measure the actual behavior of your system. Given a 0.01%<br />
normal drift rate for the clock, a drift of 8 seconds per day is not uncommon<br />
(0.01% equals 1 second in 10,000 or about 8 seconds per day). For long-term<br />
accuracy, you may need to rely on an external time source, such as a network<br />
time service as specified in Internet RFC 1305, or a radio receiver tuned to a<br />
national standard, such as WWV or WWVL.<br />
<p><br />
With all the processes competing on a Macintosh, it's possible, even likely,<br />
that several wait loops will end at the same instant, particularly on the<br />
boundaries of seconds or minutes. This can cause unpredictable delays. While<br />
the occasional long delay is not a problem for most ordinary desktop tasks, it<br />
can be devastating for systems that record or play live audio or QuickTime<br />
video. The developer of such a system must be very careful to avoid regular<br />
scheduling: all delay values (such as the sleep time passed to WaitNextEvent)<br />
should be varied by a small random value to minimize the chance of several wait<br />
loops ending at the same instant.<br />
<br />
<h3><br />
TIMING IS EVERYTHING<br />
</h3><br />
Whether you're trying to analyze the performance of your code, schedule a<br />
reminder for later, measure how long it takes users to complete a task, or<br />
remember exactly when they completed it, there's a straightforward method for<br />
doing it on the Macintosh. So go on, hook your programs up to the ever-flowing<br />
stream of time. No matter what you like to do with your time -- spend it or<br />
kill it, assess its quality or lose track of it -- your code will be able to<br />
keep pace.<p><br />
<br />
<ul><br />
<br />
<h3><br />
FURTHER READING<br />
</h3><br />
<ul><br />
<li> For information on measuring performance, see The Art of Computer Systems<br />
Performance Analysis by Raj Jain (John Wiley &amp; Sons, Inc., 1991). <br />
<br />
<p><li> TickCount, an Event Manager function, is described in <i>Inside Macintosh:<br />
Toolbox Essentials</i> (Addison-Wesley, 1992), Chapter 2, "Event Manager." Other<br />
time-measurement routines (including ReadLocation) are described in <i>Inside<br />
Macintosh: Operating System Utilities</i> (Addison-Wesley, 1994), Chapter 4, "Date,<br />
Time, and Measurement Utilities."<br />
<br />
<p><li> The extended Time Manager is described in <i>Inside Macintosh: Processes</i><br />
(Addison-Wesley, 1992), Chapter 3, "Time Manager."<br />
<br />
<p><li> UpTime and other new routines are described in <i>Designing PCI Cards and<br />
Drivers for Power Macintosh Computers</i> (Apple Computer, Inc., 1995).<br />
<br />
<p><li> The soft import capability is described in <i>Inside Macintosh: Power PC<br />
System Software</i> (Addison-Wesley, 1994), Chapter 1, "Introduction to PowerPC<br />
System Software."</ul><p><br />
<hr><br />
</ul><br />
<br />
[[Category:Apple]][[Category:Programming]]</div>Netfreak