LupineExerciserTimerImpl.mesa.
Copyright © 1985 by Xerox Corporation. All rights reserved.
Last edited by BZM on 14-Mar-82 13:24:24.
Last edited by Bob Hagmann February 8, 1985 5:09:04 pm PST
DIRECTORY
BasicTime USING [GetClockPulses, GMT, Now, Pulses, PulsesToMicroseconds],
IO USING [card, int, PutChar, PutF, PutRope, rope, STREAM, time],
LupineExerciser USING [Counter, String],
LupineExerciserPrivate USING [
CallSpy, CheckAbort, ExerciseHandle, PrecisionTime,
RingBufferIndex, SortBuffer, SortBufferIndex, SystemTime ],
PrincOps USING [zMISC],
Process USING [Yield],
UnsafeStorage USING [GetSystemUZone];
LupineExerciserTimerImpl: PROGRAM
IMPORTS
BasicTime, IO, LupineExerciserPrivate, Process, UnsafeStorage
EXPORTS LupineExerciser, LupineExerciserPrivate
= BEGIN OPEN LupineExerciser;
Private types.
SystemTime: TYPE = LupineExerciserPrivate.SystemTime;
PrecisionTime: TYPE = LupineExerciserPrivate.PrecisionTime;
RingBufferIndex: TYPE = LupineExerciserPrivate.RingBufferIndex;
Exerciser instance data.
Handle: PUBLIC TYPE = LupineExerciserPrivate.ExerciseHandle;
heap: UNCOUNTED ZONE = UnsafeStorage.GetSystemUZone[];
Primary time logging routines that use Pilot's timer.
Start: PUBLIC PROC [self: Handle, name: String] =
BEGIN OPEN tp: self.tp;
LupineExerciserPrivate.CheckAbort[self];
self.outLogStream.PutF["\n\nTiming %g trials of %g", IO.int[tp.trialsPerPass], IO.rope[name]];
TimeStamp[self, " at "];
PreparePrecisionTimings[self];
IF tp.spying THEN LupineExerciserPrivate.CallSpy[self, startSpying];
StartTimer[self: self, reset: TRUE];
END;
Stamp: PUBLIC PROC [self: Handle] =
BEGIN
StopTimer[self];
self.outLogStream.PutF["Doing %g ", IO.int[self.tp.testsPerTrial]];
TimeStamp[self, " calls at "];
StartTimer[self];
END;
Delay: PUBLIC PROC [self: Handle] =
BEGIN
StopTimer[self];
LupineExerciserPrivate.CheckAbort[self];
DoDelay[self, 600];
StartTimer[self];
END;
Stop: PUBLIC PROC [self: Handle] =
BEGIN OPEN tp: self.tp;
ms, tenths: LONG INTEGER;
StopTimer[self];
IF tp.spying THEN LupineExerciserPrivate.CallSpy[self, stopSpying];
LupineExerciserPrivate.CheckAbort[self];
IF tp.trialsPerPass = 0 OR tp.testsPerTrial = 0
THEN ms ← tenths ← 0
ELSE [ms, tenths] ← ConvertTimer[
ReadTimer[self].elapsed /
(tp.trialsPerPass*LONG[tp.testsPerTrial]) ];
self.outLogStream.PutF["The mean call time (overhead included) was %g.%g", IO.int[ms], IO.int[tenths]];
TimeStamp[self, " ms at "];
PrintPrecisionTimings[self];
IF tp.spying AND tp.showSpyData=afterEachTest
THEN LupineExerciserPrivate.CallSpy[self, displayStats];
END;
TimeStamp: PROC [self: Handle, herald: String] =
BEGIN
time: BasicTime.GMT ← BasicTime.Now[];
LupineExerciserPrivate.CheckAbort[self];
self.outLogStream.PutF["%g%g.\n", IO.rope[herald], IO.time[time]];
END;
Pilot Timing Routines.
Microseconds: TYPE = LONG CARDINAL;
ReadPilotClock: PROC RETURNS [SystemTime] =
INLINE {RETURN[ LOOPHOLE[BasicTime.GetClockPulses[]] ]};
PilotPrecisionRead: PROC RETURNS [--SystemTime-- PrecisionTime] =
INLINE { RETURN[ ReadPilotClock[] ] };
PilotPrecisionConvert: PROC [pt: --SystemTime-- PrecisionTime]
RETURNS [--microseconds:-- Microseconds] =
INLINE {RETURN[ SystemTimeToUsec[pt] ]};
StartTimer: PROC [self: Handle, reset: BOOLEANFALSE] =
INLINE BEGIN OPEN time: self.timer;
IF reset THEN time.elapsed ← 0;
time.start ← ReadPilotClock[];
END;
StopTimer: PROC [self: Handle] =
INLINE BEGIN OPEN time: self.timer;
time.event ← ReadPilotClock[] - time.start;
time.elapsed ← time.elapsed + time.event;
END;
ReadTimer: PROC [self: Handle] RETURNS [event, elapsed: SystemTime] =
INLINE BEGIN OPEN time: self.timer;
RETURN[time.event, time.elapsed]
END;
ConvertTimer: PROC [time: SystemTime] RETURNS [ms,tenthsOfMs: LONG INTEGER] =
INLINE BEGIN
time ← (SystemTimeToUsec[time]+50)/100;
ms ← time/10;
tenthsOfMs ← time MOD 10;
END;
SystemTimeToUsec: PROC [time: SystemTime] RETURNS [Microseconds] =
INLINE BEGIN
RETURN[ BasicTime.PulsesToMicroseconds[BasicTime.Pulses[time]] ]
END;
DoDelay: PROC [self: Handle, interval: --Microseconds-- LONG INTEGER] =
Spin at least "interval" between executions. There is a startup glitch.
BEGIN
LastIntegerTime: SystemTime = LAST[LONG INTEGER];
DO
grossWait: SystemTime =
SystemTimeToUsec[ReadPilotClock[]-self.timer.lastDelayFinished];
waitTime: LONG INTEGER =
IF grossWait < LastIntegerTime THEN grossWait ELSE LastIntegerTime;
SELECT interval-waitTime FROM
< 0 => EXIT;
< 50 => Process.Yield; --NULL;
ENDCASE => Process.Yield;
ENDLOOP;
self.timer.lastDelayFinished ← ReadPilotClock[];
END;
Individual call high precision timing routines.
StartPrecisionTiming: PUBLIC PROC [self: Handle] =
--INLINE-- BEGIN OPEN timer: self.precisionTimer;
timer.start ← PrecisionRead[self];
END;
StopPrecisionTiming: PUBLIC PROC [self: Handle] =
--INLINE-- BEGIN OPEN timer: self.precisionTimer;
stopTime: PrecisionTime = PrecisionRead[self];
timer.ringBuffer[
(timer.ringIndex ← (timer.ringIndex+1) MOD LENGTH[timer.ringBuffer]) ]
← (stopTime - timer.start);
END;
PrecisionRead: PROC [self: Handle] RETURNS [--pt:-- PrecisionTime] =
INLINE BEGIN
RETURN[IF self.tp.useDoradoClock
THEN DoradoPrecisionRead[self] ELSE PilotPrecisionRead[] ];
END;
PrecisionConvert: PROC [self: Handle, pt: PrecisionTime]
RETURNS [--microseconds:-- Microseconds] =
INLINE BEGIN
RETURN[IF self.tp.useDoradoClock
THEN DoradoPrecisionConvert[pt] ELSE PilotPrecisionConvert[pt] ];
END;
Precision timing routines.
InitPrecisionTimings: PUBLIC PROCEDURE [self: Handle] =
BEGIN OPEN timer: self.precisionTimer;
timer.sortBuffer ← heap.NEW[
LupineExerciserPrivate.SortBuffer[self.tp.testsPerTrial+2] ];
timer.ringBuffer ← DESCRIPTOR [
@timer.sortBuffer[FIRST[LupineExerciserPrivate.SortBufferIndex]+1],
LENGTH[DESCRIPTOR[timer.sortBuffer^]]-2 ];
END;
FinishPrecisionTimings: PUBLIC PROCEDURE [self: Handle] =
BEGIN OPEN timer: self.precisionTimer;
heap.FREE[@timer.sortBuffer];
timer.sortBuffer ← NIL;
timer.ringBuffer ← NIL;
END;
PreparePrecisionTimings: PROC [self: Handle] =
INLINE BEGIN OPEN timer: self.precisionTimer;
IF BASE[timer.ringBuffer] = NIL THEN ERROR;
timer.ringIndex ← PRED[LENGTH[timer.ringBuffer]];
FOR i: RingBufferIndex IN [0..LENGTH[timer.ringBuffer]) DO
timer.ringBuffer[i] ← 0 ENDLOOP;
IF LENGTH[timer.ringBuffer] > 0 THEN BEGIN
timer.ringBuffer[0] ← 0;
Inline.LongCopy[
from: @timer.ringBuffer[0],
to: @timer.ringBuffer[1],
nwords: LENGTH[timer.ringBuffer]*SIZE[PrecisionTime];
END;
IF self.tp.useDoradoClock THEN StartDoradoCounters[EmuCycles];
END;
PrintPrecisionTimings: PROC [self: Handle] =
BEGIN OPEN timer: self.precisionTimer;
ringBufferLength: CARDINALLENGTH[timer.ringBuffer];
Range: INTEGER = MIN[10, ringBufferLength];
PrintRange: PROC [herald: String, startIndex: RingBufferIndex] =
BEGIN
self.outLogStream.PutRope[herald];
FOR i: RingBufferIndex IN [startIndex..startIndex+Range) DO
IF timer.ringBuffer[i] > 99999
THEN self.outLogStream.PutRope[" **********"]
ELSE self.outLogStream.PutF["%6g", IO.card[timer.ringBuffer[i]]];
ENDLOOP;
self.outLogStream.PutChar['\n];
END; -- PrintRange.
FOR i: RingBufferIndex IN [FIRST[RingBufferIndex]..ringBufferLength) DO
timer.ringBuffer[i] ← PrecisionConvert[self, timer.ringBuffer[i]];
ENDLOOP;
QuickSort[DESCRIPTOR[timer.sortBuffer^]];
self.outLogStream.PutF["Some instantaneous call times (microseconds) from a %g element ring buffer:\n", IO.int[ringBufferLength]];
PrintRange[" Fastest times:", FIRST[RingBufferIndex]];
PrintRange[" Median times: ",
FIRST[RingBufferIndex] + (ringBufferLength-Range)/2 ];
PrintRange[" Slowest times:",
FIRST[RingBufferIndex]+ringBufferLength-Range];
END;
Dorado 64ns precision counter routines.
Modified from [Ivy]<McDaniel>Measurements>Measure.mesa.
doradoCounters: CounterVector ← ZeroCounterVector;
DoradoPrecisionRead: PROC [self: Handle] RETURNS [PrecisionTime] =
INLINE BEGIN
ReadDoradoCounters[@doradoCounters];
RETURN[IF self.tp.countOnlyEmulatorCycles
THEN doradoCounters.A.low ELSE doradoCounters.B.low ];
END;
DoradoPrecisionConvert: PROC [pt: PrecisionTime]
RETURNS [--microseconds:-- Microseconds] =
INLINE BEGIN
Measured clock time was 2*32.2 ns => 644/10000 = 161/2500 usec/cycle
Compute slowly to avoid overflow and do rounding.
RETURN[
(LONG[161]*(pt/LONG[2500]))
+ ( LONG[161]*(pt MOD LONG[2500])+LONG[2500/2] )/LONG[2500] ];
END;
StartDoradoCounters: PROCEDURE [CounterDescriptor] =
MACHINE CODE BEGIN PrincOps.zMISC, 240B END;
ReadDoradoCounters: PROCEDURE [CounterVectorPtr] =
MACHINE CODE BEGIN PrincOps.zMISC, 241B END;
StopDoradoCounters: PROCEDURE =
MACHINE CODE BEGIN PrincOps.zMISC, 242B END;
CounterVector: TYPE = MACHINE DEPENDENT RECORD [
A(0): MACHINE DEPENDENT RECORD [low(0): LONG CARDINAL, high(2): CARDINAL],
B(3): MACHINE DEPENDENT RECORD [low(0): LONG CARDINAL, high(2): CARDINAL] ];
ZeroCounterVector: CounterVector = [[0,0],[0,0]];
CounterVectorPtr: TYPE = LONG POINTER TO CounterVector;
ASelection: TYPE = MACHINE DEPENDENT {
true, hold, procRef, ifuJump, miss, bpa, bpc, bpe };
BSelection: TYPE = MACHINE DEPENDENT {
true, hold, ifuRef, ifuNrdy, miss, bpb, bpc, bpd };
allA, allB, doA, doB: BOOLEAN = TRUE;
emuOnlyA, emuOnlyB, notIS: BOOLEAN = FALSE;
CounterDescriptor: TYPE = MACHINE DEPENDENT RECORD [
instrSet: BOOLEAN,
ignoreBits123: [0..7],
enableA, enableB: BOOLEAN,
ignoreBits67: [0..3],
aSelects: ASelection,
allTasksA: BOOLEAN,
bSelects: BSelection,
allTasksB: BOOLEAN ];
EmuCycles: CounterDescriptor = [notIS,0,doA,doB,0,true,emuOnlyA,true,allB];
This Quicksort routine is from Sedgewick's thesis.
It is a transliteration of [Maxc2]<Guibas>STSRT.SAI.
QuickSort: PROC [table: LONG DESCRIPTOR FOR ARRAY OF PrecisionTime] =
BEGIN
first: INTEGER = 0;
last: INTEGER = LENGTH[table]-1;
M: INTEGER = 9;
PP, L, R, I, J: INTEGER;
V, TK: PrecisionTime;
STACK: ARRAY [0..20) OF INTEGER;
Originally: STACK: ARRAY [0:2*(LOG(256/(M+2)) DIV 1)+1] OF INTEGER;
table[first] ← FIRST[PrecisionTime]; table[last] ← LAST[PrecisionTime];
PP ← 0; L ← first+1; R ← last-1;
WHILE PP >=0 DO
IL; JR+1; V ← table[L];
WHILE I < J DO
II+1; WHILE table[I]<V DO II+1 ENDLOOP;
JJ-1; WHILE table[J]>V DO JJ-1 ENDLOOP;
TK←table[J]; table[J]←table[I]; table[I]←TK;
ENDLOOP;
table[I]←table[J]; table[J]←table[L]; table[L]←TK;
SELECT TRUE FROM
R-J > J-L =>
SELECT TRUE FROM
R-J <= M => NULL;
J-L <= M => {LJ+1; LOOP};
ENDCASE => BEGIN
PPPP+2; STACK[PP]←J+1; STACK[PP+1]←R; RJ-1;
LOOP;
END;
J-L <= M => NULL;
R-J <= M => {RJ-1; LOOP};
ENDCASE => BEGIN
PPPP+2; STACK[PP]←L; STACK[PP+1]←J-1; LJ+1;
LOOP;
END;
LSTACK[PP];
RSTACK[PP+1];
PPPP-2;
REPEAT
FINISHED =>
FOR I IN [2..last) DO
V←table[I]; JI-1;
WHILE table[J]>V DO table[J+1]←table[J]; JJ-1 ENDLOOP;
table[J+1]←V;
ENDLOOP;
ENDLOOP;
END;
Module Initialization.
END. -- LupineExerciserTimerImpl.