-- File LupineTimeTestDriver.mesa. -- Last edited by BZM on 2-Mar-82 21:12:38. -- Last edited by Andrew Birrell on January 26, 1983 6:08 pm -- This program is derived from Liaison's Mesa6 ParamTestDriver. DIRECTORY Heap USING [systemZone], LupineTimeTest, --USING [ALL] String USING [AppendLongNumber, AppendString, InvalidNumber, StringToDecimal], Mopcodes USING [zMISC], Process USING [Detach, Yield], Runtime USING [CallDebugger], SpyClient USING [StartSpy, StopSpy], System USING [GetClockPulses, Microseconds, Pulses, PulsesToMicroseconds], Time USING [Append, Current, Unpack], TTY USING [ Create, Destroy, GetLine, Handle, LineOverflow, NumberFormat, PutChar, PutCR, PutDecimal, PutLine, PutLongDecimal, PutLongNumber, PutString, Rubout, ResetUserAbort, UserAbort ]; LupineTimeTestDriver: PROGRAM IMPORTS TimeTest: LupineTimeTest, Heap, Process, Runtime, SpyClient, ShortString: String, System, Time, TTY = BEGIN -- Compiletime parameters: StandardNumberOfTrials: INTEGER = 1; StandardIterationsPerTrial: INTEGER = 3000; -- Runtime parameters: TestParameters: TYPE = MACHINE DEPENDENT RECORD [ useDoradoClock (0): BOOLEAN ← FALSE, countOnlyEmulatorCycles (1): BOOLEAN ← FALSE, checkResults (2): BOOLEAN ← TRUE, trials (3): INTEGER ← StandardNumberOfTrials, iterationsPerTrial (4): INTEGER ← StandardIterationsPerTrial, spying (5:0..0): BOOLEAN ← FALSE, spyOnProcs (5:1..1): BOOLEAN ← FALSE, showSpyData (5:2..2): {afterEachTest, afterAllTests} ← afterAllTests, filler (5:3..15): BOOLEAN ← NULL ]; DefaultTestParameters: TestParameters = []; -- Runtime inconsistency found: ParamsDisagree: SIGNAL = CODE; -- An echo test failed. -- This top-level driver routine is run in a separate process. user: TTY.Handle; String: TYPE = STRING; TestProcess: PROCEDURE = BEGIN logFileString: STRING = [100]; logFile: String ← UniqueName[ root: "LupineTimeTest"L, suffix: ".log"L, nameString: logFileString ]; user ← TTY.Create[name: logFile]; TestRoutine[logFile: logFile ! ABORTED => CONTINUE ]; TTY.Destroy[user]; END; UniqueName: PROCEDURE [root, suffix, nameString: String] RETURNS [rootSuffix: String] = BEGIN rootSuffix ← nameString; rootSuffix.length ← 0; ShortString.AppendString[to: rootSuffix, from: root]; ShortString.AppendLongNumber [ s: rootSuffix, n: System.GetClockPulses[], radix: 10 ]; ShortString.AppendString[to: rootSuffix, from: suffix]; END; -- For periodically causing glitches (eg, NIL) that Lupine claims to handle. Periodically: PROC [testNumber: INTEGER] RETURNS [--yes:-- BOOLEAN] = --INLINE-- {RETURN[ (testNumber MOD 31) = 0 ]}; -- Main Timing Test. tp: TestParameters; -- Global values. TestRoutine: PROCEDURE[logFile: String] = BEGIN -- The entire test is in a loop. InitPrecisionTimings; DO ENABLE BEGIN AbortTest => CONTINUE; UNWIND => FinishPrecisionTimings; END; -- Get Test Parameters. tp ← DefaultTestParameters; TTY.PutCR[user]; TTY.PutLine[user, "Lupine RPC Timing Test of 24 February 1982."L]; TTY.PutString[user, "Results appear in file "L]; TTY.PutString[user, logFile]; TimeStamp[" of "L]; TTY.PutCR[user]; TTY.PutString[user, "Your options are: D Don't check results of remote calls for correctness. E Exclude nonemulator cycles in timings (Dorados only). G Go (type this last). H High precision timings (Dorados only). P Procedure-level spying (via Copilot). R Reset test parameters to default values. S Spy after all tests are over. T Spy at the conclusion of each test. Q Quit this program immediately. -- This line is a comment. 1-9 Perform # trials of each test. ↑DEL To abort at any time."L ]; TTY.PutCR[user]; TTY.PutCR[user]; DO OPEN tp, TTY; BEGIN reply: STRING = [100]; PutString[user, "Type one of D,E,G,H,P,R,S,T,Q,--,1-9 [R1]: "L]; reply[0] ← 0C; CheckAbort; GetLine[user, reply ! Rubout, LineOverflow => GOTO TryAgain]; CheckAbort; SELECT reply[0] FROM 'd, 'D => {checkResults ← FALSE}; 'e, 'E => {useDoradoClock ← countOnlyEmulatorCycles ← TRUE}; 'g, 'G => {EXIT}; 'h, 'H => {useDoradoClock ← TRUE}; 'p, 'P => {spying ← spyOnProcs ← TRUE}; 'r, 'R => {tp ← DefaultTestParameters}; 's, 'S => {spying ← TRUE; showSpyData ← afterAllTests}; 't, 'T => {spying ← TRUE; showSpyData ← afterEachTest}; 'q, 'Q => {GOTO QuitTestProgram}; '- => {NULL}; IN ['1..'9] => {trials ← ShortString.StringToDecimal[ reply ! ShortString.InvalidNumber => GOTO TryAgain ]}; ENDCASE => GOTO TryAgain; EXITS TryAgain => PutLine[user, " ???"L]; END; ENDLOOP; -- Call debugger as needed for spying. IF tp.spying THEN CallSpy[ IF tp.spyOnProcs THEN startAndWatchDetails ELSE start ]; -- Eliminating timing transients before timings start. TimeTest.Null[]; TimeTest.Null[]; TimeTest.Null[]; -- Perform testing. NullTests; SimpleParameterTests; SignalTests; SmallArrayTests; --BigArrayTests; -- Call debugger as needed for spying. IF tp.spying THEN CallSpy[ (SELECT tp.showSpyData FROM afterEachTest => stop, afterAllTests => stopAndDisplayStats, ENDCASE => ERROR) ]; REPEAT QuitTestProgram => NULL; ENDLOOP; -- Test Loop. FinishPrecisionTimings; END; -- TestRoutine. NullTests: PROC = BEGIN -- Timing overhead. StartTest[testName: "per call timing overhead"L, skipSpy: TRUE]; THROUGH [0..tp.trials) DO StampTest; FOR test: INTEGER IN [0..tp.iterationsPerTrial) DO --DelayTest; StartPrecisionTiming; NULL; StopPrecisionTiming; ENDLOOP; ENDLOOP; StopTest[skipSpy: TRUE]; -- Null Procedure StartTest["the null procedure"L]; THROUGH [0..tp.trials) DO StampTest; FOR test: INTEGER IN [0..tp.iterationsPerTrial) DO DelayTest; StartPrecisionTiming; TimeTest.Null[]; StopPrecisionTiming; ENDLOOP; ENDLOOP; StopTest; END; -- NullTests. SimpleParameterTests: PROC = BEGIN a,b,c,d,e,f,g,h,i,j: INTEGER; StartTest["a 1 parameter procedure"L]; THROUGH [0..tp.trials) DO StampTest; FOR test: INTEGER IN [0..tp.iterationsPerTrial) DO DelayTest; StartPrecisionTiming; a ← TimeTest.One[test]; StopPrecisionTiming; IF tp.checkResults AND a#test THEN SIGNAL ParamsDisagree; ENDLOOP; ENDLOOP; StopTest; StartTest["a 2 parameter procedure"L]; THROUGH [0..tp.trials) DO StampTest; FOR test: INTEGER IN [0..tp.iterationsPerTrial) DO DelayTest; StartPrecisionTiming; [a,b] ← TimeTest.Two[test+0,test+1]; StopPrecisionTiming; IF tp.checkResults AND (a#test+0 OR b#test+1) THEN SIGNAL ParamsDisagree; ENDLOOP; ENDLOOP; StopTest; StartTest["a 4 parameter procedure"L]; THROUGH [0..tp.trials) DO StampTest; FOR test: INTEGER IN [0..tp.iterationsPerTrial) DO DelayTest; StartPrecisionTiming; [a,b,c,d] ← TimeTest.Four[test+0,test+1,test+2,test+3]; StopPrecisionTiming; IF tp.checkResults AND (a#test+0 OR b#test+1 OR c#test+2 OR d#test+3) THEN SIGNAL ParamsDisagree; ENDLOOP; ENDLOOP; StopTest; StartTest["a 10 parameter procedure"L]; THROUGH [0..tp.trials) DO StampTest; FOR test: INTEGER IN [0..tp.iterationsPerTrial) DO DelayTest; StartPrecisionTiming; [a,b,c,d,e,f,g,h,i,j] ← TimeTest.Ten[ test+0,test+1,test+2,test+3,test+4, test+5,test+6,test+7,test+8,test+9 ]; StopPrecisionTiming; IF tp.checkResults AND (a#test+0 OR b#test+1 OR c#test+2 OR d#test+3 OR e#test+4 OR f#test+5 OR g#test+6 OR h#test+7 OR i#test+8 OR j#test+9) THEN SIGNAL ParamsDisagree; ENDLOOP; ENDLOOP; StopTest; END; -- SimpleParameterTests. SignalTests: PROC = BEGIN out: INTEGER; StartTest["signal test (no signalling)"L]; THROUGH [0..tp.trials) DO StampTest; FOR test: INTEGER IN [0..tp.iterationsPerTrial) DO DelayTest; StartPrecisionTiming; out ← TimeTest.SignalTest[ in: test, action: neither ! TimeTest.Signal => {SIGNAL ParamsDisagree; CONTINUE} ]; StopPrecisionTiming; IF tp.checkResults AND out#test THEN SIGNAL ParamsDisagree; ENDLOOP; ENDLOOP; StopTest; StartTest["signal test (SIGNAL & RESUME)"L]; THROUGH [0..tp.trials) DO StampTest; FOR test: INTEGER IN [0..tp.iterationsPerTrial) DO DelayTest; StartPrecisionTiming; out ← TimeTest.SignalTest[ in: test, action: signal ! TimeTest.Signal => { IF tp.checkResults AND in#test THEN SIGNAL ParamsDisagree; RESUME[out: 2*in] } ]; StopPrecisionTiming; IF tp.checkResults AND out#2*test THEN SIGNAL ParamsDisagree; ENDLOOP; ENDLOOP; StopTest; StartTest["signal test (ERROR & UNWIND)"L]; THROUGH [0..tp.trials) DO StampTest; FOR test: INTEGER IN [0..tp.iterationsPerTrial) DO DelayTest; StartPrecisionTiming; out ← TimeTest.SignalTest[ in: test, action: error ! TimeTest.Signal => { IF tp.checkResults AND in#test THEN SIGNAL ParamsDisagree; CONTINUE } ]; StopPrecisionTiming; --Checking is never valid after an ERROR since out is undefined. ENDLOOP; ENDLOOP; StopTest; END; -- SignalTests. SmallArrayTests: PROC = BEGIN one: TimeTest.Array1 = ALL[1]; four: TimeTest.Array4 = ALL[4]; ten: TimeTest.Array10 = ALL[10]; forty: TimeTest.Array40 = ALL [40]; hundred: TimeTest.Array100 = ALL[100]; array1: TimeTest.Array1; array4: TimeTest.Array4; array10: TimeTest.Array10; array40: TimeTest.Array40; array100: TimeTest.Array100; StartTest["1 word array procedure"L]; THROUGH [0..tp.trials) DO StampTest; FOR test: INTEGER IN [0..tp.iterationsPerTrial) DO DelayTest; StartPrecisionTiming; array1 ← TimeTest.OneArray[one]; StopPrecisionTiming; IF tp.checkResults AND one#array1 THEN SIGNAL ParamsDisagree; ENDLOOP; ENDLOOP; StopTest; StartTest["4 word array procedure"L]; THROUGH [0..tp.trials) DO StampTest; FOR test: INTEGER IN [0..tp.iterationsPerTrial) DO DelayTest; StartPrecisionTiming; array4 ← TimeTest.FourArray[four]; StopPrecisionTiming; IF tp.checkResults AND four#array4 THEN SIGNAL ParamsDisagree; ENDLOOP; ENDLOOP; StopTest; StartTest["10 word array procedure"L]; THROUGH [0..tp.trials) DO StampTest; FOR test: INTEGER IN [0..tp.iterationsPerTrial) DO DelayTest; StartPrecisionTiming; array10 ← TimeTest.TenArray[ten]; StopPrecisionTiming; IF tp.checkResults AND ten#array10 THEN SIGNAL ParamsDisagree; ENDLOOP; ENDLOOP; StopTest; StartTest["40 word array procedure"L]; THROUGH [0..tp.trials) DO StampTest; FOR test: INTEGER IN [0..tp.iterationsPerTrial) DO DelayTest; StartPrecisionTiming; array40 ← TimeTest.FortyArray[forty]; StopPrecisionTiming; IF tp.checkResults AND forty#array40 THEN SIGNAL ParamsDisagree; ENDLOOP; ENDLOOP; StopTest; StartTest["100 word array procedure"L]; THROUGH [0..tp.trials) DO StampTest; FOR test: INTEGER IN [0..tp.iterationsPerTrial) DO DelayTest; StartPrecisionTiming; array100 ← TimeTest.HundredArray[hundred]; StopPrecisionTiming; IF tp.checkResults AND hundred#array100 THEN SIGNAL ParamsDisagree; ENDLOOP; ENDLOOP; StopTest; END; -- SmallArrayTests. -- Primary time logging routines that use Pilot's timer. StartTest: PROC [testName: String, skipSpy: BOOLEAN←FALSE] = BEGIN OPEN TTY; CheckAbort; PutCR[user]; PutCR[user]; PutString[user, "Timing "L]; PutDecimal[user, tp.trials]; PutString[user, " trials of "L]; PutString[user, testName]; TimeStamp[" at "L]; PreparePrecisionTimings; IF ~skipSpy AND tp.spying THEN CallSpy[startSpy]; StartTimer[reset:TRUE]; END; StampTest: PROC = INLINE {StopTimer; DoStampTest; StartTimer}; DoStampTest: PROC = BEGIN OPEN TTY; PutString[user, "Doing "L]; PutLongDecimal[user, tp.iterationsPerTrial]; TimeStamp[" calls at "L]; END; DelayTest: PROC = INLINE BEGIN StopTimer; CheckAbort; Delay[1000]; StartTimer; END; StopTest: PROC [skipSpy: BOOLEAN←FALSE] = BEGIN OPEN TTY; ms, tenths: LONG INTEGER; StopTimer[]; IF ~skipSpy AND tp.spying THEN CallSpy[stopSpy]; CheckAbort; [ms, tenths] ← ConvertTimer[ReadTimer[].elapsed/(tp.trials*LONG[tp.iterationsPerTrial])]; PutString[user, "The average call time (any checking included) was "L]; PutLongDecimal[user, ms]; PutChar[user, '.]; PutLongDecimal[user, tenths]; TimeStamp[" ms at "L]; PrintPrecisionTimings; IF ~skipSpy AND tp.spying AND tp.showSpyData=afterEachTest THEN CallSpy[displayStats]; END; TimeStamp: PROC [herald: String] = BEGIN OPEN TTY; timeString: STRING = [100]; CheckAbort; Time.Append[timeString, Time.Unpack[Time.Current[]]]; PutString[user, herald]; PutString[user, timeString]; PutLine[user, "."L]; END; AbortTest: PRIVATE ERROR = CODE; -- Raised by CheckAbort and caught in TestRoutine. CheckAbort: PROC = BEGIN OPEN TTY; IF ~UserAbort[] THEN RETURN; ResetUserAbort; PutCR[user]; PutLine[user, "Test aborted..."L]; ERROR AbortTest; END; -- Pilot Timing Routines. SystemTime: TYPE = LONG CARDINAL; -- Same as System.Pulses. Microseconds: TYPE = LONG CARDINAL; ReadPilotClock: PROC RETURNS [SystemTime] = INLINE {RETURN[ LOOPHOLE[System.GetClockPulses[]] ]}; PilotPrecisionRead: PROC RETURNS [--SystemTime-- PrecisionTime] = INLINE { RETURN[ ReadPilotClock[] ] }; PilotPrecisionConvert: PROC [pt: --SystemTime-- PrecisionTime] RETURNS [--microseconds:-- Microseconds] = ----INLINE---- {RETURN[ SystemTimeToUsec[pt] ]}; hrStartTime, hrEventTime, hrElapsedTime: SystemTime ← 0; StartTimer: PROC [reset: BOOLEAN←FALSE] = INLINE BEGIN IF reset THEN hrElapsedTime ← 0; hrStartTime ← ReadPilotClock[]; END; StopTimer: PROC = INLINE BEGIN hrEventTime ← ReadPilotClock[] - hrStartTime; hrElapsedTime ← hrElapsedTime + hrEventTime; END; ReadTimer: PROC RETURNS [event, elapsed: SystemTime] = ----INLINE---- BEGIN RETURN[hrEventTime, hrElapsedTime] END; ConvertTimer: PROC [time: SystemTime] RETURNS [ms,tenthsOfMs: LONG INTEGER] = BEGIN time ← (SystemTimeToUsec[time]+50)/100; ms ← time/10; tenthsOfMs ← time MOD 10; END; SystemTimeToUsec: PROC [time: SystemTime] RETURNS [Microseconds] = INLINE BEGIN RETURN[ System.PulsesToMicroseconds[System.Pulses[time]] ] END; lastDelayFinished: SystemTime ← 0; Delay: PROC [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[]-lastDelayFinished]; waitTime: LONG INTEGER = IF grossWait < LastIntegerTime THEN grossWait ELSE LastIntegerTime; SELECT interval-waitTime FROM < 0 => EXIT; < 50 => NULL; ENDCASE => Process.Yield; ENDLOOP; lastDelayFinished ← ReadPilotClock[]; END; -- Individual call high precision timing routines. PrecisionTime: TYPE = LONG CARDINAL; RingBufferIndex: TYPE = INTEGER[0..StandardIterationsPerTrial); SortBufferIndex: TYPE = INTEGER[0..LAST[RingBufferIndex]+2); RingBuffer: TYPE = ARRAY RingBufferIndex OF PrecisionTime; SortBuffer: TYPE = ARRAY SortBufferIndex OF PrecisionTime; ptRingIndex: RingBufferIndex ← LAST[RingBufferIndex]; ptRingBuffer: LONG DESCRIPTOR FOR RingBuffer ← DESCRIPTOR [NIL, 0]; ptSortBuffer: LONG DESCRIPTOR FOR SortBuffer ← DESCRIPTOR [NIL, 0]; ptStartTime: PrecisionTime; StartPrecisionTiming: PROC = INLINE BEGIN ptStartTime ← PrecisionRead[]; END; StopPrecisionTiming: PROC = INLINE BEGIN ptStopTime: PrecisionTime = PrecisionRead[]; ptRingBuffer[(ptRingIndex ← (ptRingIndex+1) MOD (LAST[RingBufferIndex]+1))] ← (ptStopTime - ptStartTime); END; PrecisionRead: PROC RETURNS [--pt:-- PrecisionTime] = INLINE BEGIN RETURN[IF tp.useDoradoClock THEN DoradoPrecisionRead[] ELSE PilotPrecisionRead[] ]; END; PrecisionConvert: PROC [pt: PrecisionTime] RETURNS [--microseconds:-- Microseconds] = ----INLINE---- BEGIN RETURN[IF tp.useDoradoClock THEN DoradoPrecisionConvert[pt] ELSE PilotPrecisionConvert[pt] ]; END; -- Precision timing routines. InitPrecisionTimings: PROC = BEGIN -- A compiler LENGTH computation bug makes this ugly: bufferPtr: LONG POINTER TO SortBuffer ← Heap.systemZone.NEW[SortBuffer]; ptSortBuffer ← DESCRIPTOR[bufferPtr, LENGTH[bufferPtr↑]]; ptRingBuffer ← DESCRIPTOR [ @ptSortBuffer[FIRST[SortBufferIndex]+1], LENGTH[LOOPHOLE[NIL, POINTER TO RingBuffer]↑] ]; END; FinishPrecisionTimings: PROC = BEGIN bufferPtr: LONG POINTER TO SortBuffer ← BASE[ptSortBuffer]; Heap.systemZone.FREE[@bufferPtr]; ptSortBuffer ← NIL; ptRingBuffer ← NIL; END; PreparePrecisionTimings: PROC = BEGIN IF BASE[ptRingBuffer] = NIL THEN ERROR; ptRingIndex ← LAST[RingBufferIndex]; FOR i: RingBufferIndex IN RingBufferIndex DO ptRingBuffer[i] ← 0 ENDLOOP; IF tp.useDoradoClock THEN StartDoradoCounters[EmuCycles]; END; PrintPrecisionTimings: PROC = BEGIN OPEN TTY; Range: INTEGER = 10; PrintRange: PROC [herald: String, startIndex: RingBufferIndex] = BEGIN PutString[user, herald]; FOR i: RingBufferIndex IN [startIndex..startIndex+Range) DO IF ptRingBuffer[i] > 99999 THEN PutString[user, " *****"L] ELSE PutLongNumber[ h: user, n: ptRingBuffer[i], format: [base: 10, zerofill: FALSE, unsigned: TRUE, columns: 6] ]; ENDLOOP; PutCR[user]; END; -- PrintRange. FOR i: RingBufferIndex IN RingBufferIndex DO ptRingBuffer[i] ← PrecisionConvert[ptRingBuffer[i]]; ENDLOOP; QuickSort[ptSortBuffer]; PutString[user, "Some instantaneous call times (microseconds) from a "L]; PutDecimal[user, LAST[RingBufferIndex]-FIRST[RingBufferIndex]+1]; PutLine[user, " element ring buffer:"L]; PrintRange[" Fastest times:"L, FIRST[RingBufferIndex]]; PrintRange[" Median times: "L, (LAST[RingBufferIndex]-FIRST[RingBufferIndex]-Range)/2]; PrintRange[" Slowest times:"L, LAST[RingBufferIndex]-Range]; END; -- Dorado 64ns precision counter routines. -- Modified from [Ivy]<McDaniel>Measurements>Measure.mesa. doradoCounters: CounterVector ← ZeroCounterVector; DoradoPrecisionRead: PROC RETURNS [PrecisionTime] = INLINE BEGIN ReadDoradoCounters[@doradoCounters]; RETURN[IF 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 Mopcodes.zMISC, 240B END; ReadDoradoCounters: PROCEDURE [CounterVectorPtr] = MACHINE CODE BEGIN Mopcodes.zMISC, 241B END; StopDoradoCounters: PROCEDURE = MACHINE CODE BEGIN Mopcodes.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..50) 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 I ← L; J ← R+1; V ← table[L]; WHILE I < J DO I←I+1; WHILE table[I]<V DO I ← I+1 ENDLOOP; J←J-1; WHILE table[J]>V DO J ← J-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 => {L←J+1; LOOP}; ENDCASE => BEGIN PP←PP+2; STACK[PP]←J+1; STACK[PP+1]←R; R←J-1; LOOP; END; J-L <= M => NULL; R-J <= M => {R←J-1; LOOP}; ENDCASE => BEGIN PP ← PP+2; STACK[PP]←L; STACK[PP+1]←J-1; L←J+1; LOOP; END; L←STACK[PP]; R←STACK[PP+1]; PP←PP-2; REPEAT FINISHED => FOR I IN [2..last) DO V←table[I]; J←I-1; WHILE table[J]>V DO table[J+1]←table[J]; J←J-1 ENDLOOP; table[J+1]←V; ENDLOOP; ENDLOOP; END; -- General spy routine, in the hope that the real procedural interface improves. CallSpy: PROCEDURE[operation: { start, startAndWatchDetails, startSpy, stopSpy, displayStats, stop, stopAndDisplayStats } ] = BEGIN SELECT operation FROM start => Runtime.CallDebugger["Start Spy..."L]; startAndWatchDetails => Runtime.CallDebugger["Start Spy and finger specific procs..."L]; startSpy => SpyClient.StartSpy; stopSpy => SpyClient.StopSpy; displayStats => Runtime.CallDebugger["Examine Spy statistics..."L]; stop => Runtime.CallDebugger["Stop Spy..."L]; stopAndDisplayStats => Runtime.CallDebugger["Stop Spy and examine statistics..."L]; ENDCASE => ERROR; END; -- Module Initialization. Process.Detach[FORK TestProcess[]]; END. -- LupineTimeTestDriver. BigArrayTests: PROC = BEGIN fourHundred: TimeTest.Array400 = ALL[400]; thousand: TimeTest.Array1000 = ALL[1000]; array400: TimeTest.Array400; array1000: TimeTest.Array1000; StartTest["400 word array procedure"L]; THROUGH [0..tp.trials) DO StampTest; FOR test: INTEGER IN [0..tp.iterationsPerTrial) DO DelayTest; StartPrecisionTiming; array400 ← TimeTest.FourHundredArray[fourHundred]; StopPrecisionTiming; IF tp.checkResults AND fourHundred#array400 THEN SIGNAL ParamsDisagree; ENDLOOP; ENDLOOP; StopTest; StartTest["1000 word array procedure"L]; THROUGH [0..tp.trials) DO StampTest; FOR test: INTEGER IN [0..tp.iterationsPerTrial) DO DelayTest; StartPrecisionTiming; array1000 ← TimeTest.ThousandArray[thousand]; StopPrecisionTiming; IF tp.checkResults AND thousand#array1000 THEN SIGNAL ParamsDisagree; ENDLOOP; ENDLOOP; StopTest; END; -- BigArrayTests. -- String and array descriptor test. Alphabet: TYPE = PACKED ARRAY [0..26) OF CHARACTER; string1: String = "AbCdEfGhIjKlMnOpQrStUvWxYz"L; string2: String = "12345678901234567890123456"L; text: Alphabet; textDescriptor: TimeTest.RESULTTextDescriptor = DESCRIPTOR[text]; StartTest["26 character string to sequence procedure"L]; THROUGH [0..tp.trials) DO StampTest; FOR test: INTEGER IN [0..tp.iterationsPerTrial) DO string: String = IF test MOD 2 = 0 THEN string1 ELSE string2; DelayTest; StartPrecisionTiming; TimeTest.StringDescriptor[string, textDescriptor]; StopPrecisionTiming; IF tp.checkResults AND LOOPHOLE[@string.text, LONG POINTER TO Alphabet]↑ # LOOPHOLE[BASE[textDescriptor], LONG POINTER TO Alphabet]↑ THEN SIGNAL ParamsDisagree; ENDLOOP; ENDLOOP; StopTest;