-- File PerfStatsImpl.mesa -- Last edited by: -- MBrown on February 7, 1984 3:13:36 pm PST DIRECTORY IO, BasicTime, PerfStats, Rope; PerfStatsImpl: CEDAR PROGRAM IMPORTS IO, BasicTime, Rope EXPORTS PerfStats SHARES PerfStats = BEGIN ROPE: TYPE = Rope.ROPE; STREAM: TYPE = IO.STREAM; -- Exported type Timer: TYPE = REF TimerObject; TimerObject: PUBLIC TYPE = RECORD[ pName: ROPE, TimerWasStarted: BOOLEAN, TimeWhenStarted: LONG CARDINAL ← NULL, TotalElapsedTime: LONG CARDINAL ← NULL, MaxTime: LONG CARDINAL ← NULL, MinTime: LONG CARDINAL ← NULL, NStopTimerCalls: LONG CARDINAL ← NULL, next: Timer]; Counter: TYPE = PerfStats.Counter; CounterObject: TYPE = PerfStats.CounterObject; -- Module state counterList: Counter ← NIL; timerList: Timer ← NIL; nGlitches: LONG CARDINAL ← 0; -- Counts number of improperly-matched Start - Stop calls. -- Procedures (also see inlines in PerfStats). CreateCounter: PUBLIC PROC[name: ROPE] RETURNS[Counter] = { TestForDuplicate: PROC [e: Counter] = { IF name.Equal[e.pName] THEN ERROR DuplicateName}; EnumerateCounters[TestForDuplicate]; {e: Counter ← NEW[CounterObject ← [pName: name, next: counterList]]; InitializeCounter[e]; RETURN[counterList ← e] }}; DuplicateName: PUBLIC ERROR = CODE; InitializeCounter: PUBLIC PROC[event: Counter] = { event.counter ← 0 }; DestroyCounter: PUBLIC PROC[event: Counter] = { Remove: PROC [e: Counter] = { IF e.next = event THEN e.next ← event.next }; IF event = counterList THEN counterList ← event.next ELSE EnumerateCounters[Remove]; event.pName ← NIL; event.next ← NIL }; EnumerateCounters: PROC[procToApply: PROC[Counter]] = { FOR p: Counter ← counterList, p.next UNTIL p=NIL DO procToApply[p] ENDLOOP }; CreateTimer: PUBLIC PROC[name: ROPE] RETURNS[Timer] = { TestForDuplicate: PROC [e: Timer] = { IF name.Equal[e.pName] THEN ERROR DuplicateName}; EnumerateTimers[TestForDuplicate]; {e: Timer ← NEW[TimerObject ← [pName: name, TimerWasStarted: FALSE, next: timerList]]; InitializeTimer[e]; RETURN[timerList ← e] }}; InitializeTimer: PUBLIC PROC[event: Timer] = { -- Don't reset a running timer. event.TotalElapsedTime ← 0; event.MaxTime ← 0; event.MinTime ← LAST[LONG CARDINAL]; event.NStopTimerCalls ← 0 }; DestroyTimer: PUBLIC PROC[event: Timer] = { Remove: PROC[e: Timer] = { IF e.next = event THEN e.next ← event.next }; IF event = timerList THEN timerList ← event.next ELSE EnumerateTimers[Remove]; event.pName ← NIL; event.next ← NIL }; EnumerateTimers: PROC[procToApply: PROC[Timer]] = { FOR p: Timer ← timerList, p.next UNTIL p=NIL DO procToApply[p] ENDLOOP }; Initialize: PUBLIC PROC[] = { EnumerateCounters[InitializeCounter]; EnumerateTimers[InitializeTimer] }; Start: PUBLIC PROC[event: Timer] = { IF event.TimerWasStarted THEN nGlitches ← nGlitches + 1; event.TimerWasStarted ← TRUE; event.TimeWhenStarted ← BasicTime.GetClockPulses[] }; Stop: PUBLIC PROC[event: Timer] = { IF ~event.TimerWasStarted THEN { nGlitches ← nGlitches + 1; RETURN }; event.TimerWasStarted ← FALSE; event.NStopTimerCalls ← event.NStopTimerCalls + 1; -- What is done next is a function of how elaborate the stats need to be. It might --even be a function of event. For now, do something simple. {elapsedTime: LONG CARDINAL ← BasicTime.GetClockPulses[] - event.TimeWhenStarted; event.TotalElapsedTime ← event.TotalElapsedTime + elapsedTime; event.MaxTime ← MAX[elapsedTime, event.MaxTime]; event.MinTime ← MIN[elapsedTime, event.MinTime] } }; Print: PUBLIC PROC [heading: ROPE, oStream: STREAM, verbose: BOOLEAN] = { PrintHighResolutionTime: PROC [time: LONG CARDINAL] RETURNS [BOOL] = { -- Returns TRUE iff time is less than 2 ms. time ← BasicTime.PulsesToMicroseconds[time]/100; -- convert to 1/10 ms units {ms: LONG CARDINAL ← time/10; decimal: LONG CARDINAL ← time MOD 10; oStream.PutF["%g.%g", IO.card[ms], IO.card[decimal]]; RETURN[ms <= 1]}; }; PrintCounter: PROC[e: Counter] = { IF e.counter > 0 THEN { oStream.PutF["%22g: %g events\n", IO.rope[e.pName], IO.card[e.counter]] } ELSE IF verbose THEN { oStream.PutF["%22g: no events\n", IO.rope[e.pName]] } }; PrintTimer: PROC[e: Timer] = { IF e.NStopTimerCalls > 0 THEN { timeIsSmall: BOOLEAN; oStream.PutF["%22g: ", IO.rope[e.pName]]; timeIsSmall ← PrintHighResolutionTime[e.TotalElapsedTime/e.NStopTimerCalls]; IF timeIsSmall THEN { avgTime: LONG CARDINAL ← BasicTime.PulsesToMicroseconds[ e.TotalElapsedTime/e.NStopTimerCalls]; oStream.PutF[" ms (%g us) average time for %g events.", IO.card[avgTime], IO.card[e.NStopTimerCalls]] } ELSE { oStream.PutF[" ms average time for %g events.", IO.card[e.NStopTimerCalls]] }; oStream.PutF[" Max = "]; [] ← PrintHighResolutionTime[e.MaxTime]; oStream.PutF[", Min = "]; [] ← PrintHighResolutionTime[e.MinTime]; oStream.PutF[".\n"] } ELSE IF verbose THEN { oStream.PutF["%g: no events\n", IO.rope[e.pName]] }; }; IF heading = NIL OR heading.Size[] = 0 THEN heading ← "PerfStats"; oStream.PutF[IF verbose THEN "%g (printing all events at %t)\n" ELSE "%g (printing nonzero events at %t)\n", IO.rope[heading], IO.time[]]; EnumerateCounters[PrintCounter]; EnumerateTimers[PrintTimer]; IF nGlitches > 0 THEN oStream.PutF["?%g out of order calls to Starting or Stopping!\n", IO.card[nGlitches]]; nGlitches ← 0; oStream.PutF["\n\n"]; oStream.Flush[]; }; END.--PerfStatsImpl CHANGE LOG Created by MBrown on November 4, 1980 4:23 PM -- By editing DBStatsImpl. Changed by MBrown on November 6, 1980 3:30 PM -- Uses DBLogStream instead of its own internal stream, to allow output --to go to same file as DBStatsImpl. Changed by MBrown on November 7, 1980 9:31 AM -- Make InitializeCounterEvent and InitializeTimerEvent public. Changed by MBrown on November 7, 1980 4:26 PM -- Fix Print to make output take fewer lines. Changed by MBrown on November 10, 1980 1:11 PM -- Make ReadClock for Alto I faster (marginal improvement). When average time is small, --print it in microseconds. Changed by MBrown on December 8, 1980 6:21 PM -- Don't reset running timers in InitializeTimerEvent. Changed by MBrown on January 10, 1981 9:29 PM -- Created Pilot/collectible storage version. Print now takes putChar and cleanup procs as parms. --Renamed to PerfStatsImpl. Changed by MBrown on January 11, 1981 5:02 PM -- Added DuplicateName ERROR. Added verbose parm to Print. Changed by MBrown on 18-Aug-81 18:46:34 -- CedarString -> Rope (ugly since CWF does not know about Rope.) Changed by MBrown on 7-Dec-81 16:02:11 -- Convert to use IOStream. Changed by MBrown on 16-Feb-82 8:58:33 -- Remove LOOPHOLEs in dealing with times (compiler bug fixed.) Changed by MBrown on June 24, 1982 1:21 pm -- IOStream -> IO, CEDAR. System procs GetClockPulses, PulsesToMicroseconds need to be SAFE. Changed by MBrown on February 7, 1984 3:13:26 pm PST -- Cedar 5.1.