startTime: BasicTime.GMT;
GopherIt:
PROC =
TRUSTED {
head: LONG STRING ← "Starting major loop ...";
header: ROPE ← "Starting major loop ...";
headSize: CARDINAL ← head.length;
dummy: REF ← NIL;
start: INT ← 0;
c: CHAR ← 0C;
operationIndex: NAT ← 0;
str: STRING ← [20];
mark: Pulses ← BasicTime.GetClockPulses[];
InitMark:
PROC =
TRUSTED {
mark ← BasicTime.GetClockPulses[];
faults ← VMStatistics.pageFaults;
};
TestOne:
PROC
[inner: PROC, operations: INT ← 100, dummyProcVar: PROC ← NIL]
RETURNS [timePerOperation: REAL ← 0.0] = TRUSTED {
elapsed: Microseconds ← 0;
overhead: Microseconds ← 0;
temp: Pulses ← 0;
divisor: REAL ← 1.0;
IF dummyProcVar = NIL THEN dummyProcVar ← dummyProc;
make sure that the IO system is pretty quiet
IO.Flush[stream];
try the initial flush of things into our cache
dummyProcVar[];
inner[];
higher priority makes our numbers somewhat sharper
Process.SetPriority[Process.GetPriority[]+2];
WHILE elapsed < 1000
DO
divisor ← operations;
try to flush the dummyProc into the caches
dummyProcVar[];
dummyProcVar[];
calc the overhead time
temp ← BasicTime.GetClockPulses[];
temp ← BasicTime.GetClockPulses[];
FOR i:
INT
IN [0..operations)
DO
dummyProcVar[];
ENDLOOP;
temp ← BasicTime.GetClockPulses[] - temp;
overhead ← BasicTime.PulsesToMicroseconds[temp];
calc the operation time
IF operations > 3
THEN {
inner[]; -- do once to get some 1st-time effects over
inner[]}; -- do once more to get some 2nd-time effects over
temp ← BasicTime.GetClockPulses[];
temp ← BasicTime.GetClockPulses[];
FOR i:
INT
IN [0..operations)
DO
inner[];
ENDLOOP;
temp ← BasicTime.GetClockPulses[] - temp;
elapsed ← BasicTime.PulsesToMicroseconds[temp];
IF elapsed > overhead THEN elapsed ← elapsed - overhead ELSE elapsed ← 0;
IF operations > 10000 AND elapsed = 0 THEN ERROR;
increase the number of iterations
operations ← operations * 10;
ENDLOOP;
priority is less important now...
Process.SetPriority[Process.GetPriority[]-2];
timePerOperation ← LOOPHOLE[elapsed, INT];
timePerOperation ← timePerOperation / divisor;
};
Report:
PROC
[inner: PROC, msg: ROPE, operations: INT ← 100, dummyProcVar: PROC ← NIL]
= TRUSTED {
faults: INT ← VMStatistics.pageFaults;
timePerOperation: REAL ← TestOne[inner, operations, dummyProcVar];
bestTime: REAL ← best[operationIndex];
IF bestTime > timePerOperation THEN best[operationIndex] ← bestTime ← timePerOperation;
operationIndex ← operationIndex + 1;
faults ← VMStatistics.pageFaults - faults;
WriteRope[" "]; WriteReal[timePerOperation];
WriteRope[" usecs ["]; WriteReal[bestTime]; WriteRope["] "];
IF faults > 0
THEN {
WriteRope[" ("]; WriteDecimal[faults];
IF faults = 1 THEN WriteRope[" fault) "] ELSE WriteRope[" faults) "]};
WriteLine[msg];
};
dummyProc: PROC = TRUSTED {i: INT ← int; {}}; -- one up-level access
dummyProc0: PROC = TRUSTED {}; -- really nothing!!!
innerBool1:
PROC =
TRUSTED {
b: BOOL ← TRUE;
IF b THEN {b ← TRUE};
};
innerBool2:
PROC =
TRUSTED {
b: BOOL ← FALSE;
IF b THEN {b ← TRUE};
};
innerNarrow:
PROC =
TRUSTED {
ref: REF ← header;
r: ROPE ← NARROW[ref];
};
innerRefSelect1:
PROC =
TRUSTED {
ref: REF ← header;
WITH ref
SELECT
FROM
x: ROPE => {};
ENDCASE => ERROR;
};
innerRefSelect2:
PROC =
TRUSTED {
ref: REF ← header;
WITH ref
SELECT
FROM
a: ATOM => {};
x: ROPE => {};
ENDCASE => ERROR;
};
innerRefSelect3:
PROC =
TRUSTED {
ref: REF ← header;
WITH ref
SELECT
FROM
a: ATOM => {};
l: LIST OF REF => {};
x: ROPE => {};
ENDCASE => ERROR;
};
innerVariantSelect:
PROC =
TRUSTED {
ptr: LONG POINTER TO Sky ← @sky;
WITH x: ptr
SELECT
FROM
red => {};
ENDCASE => ERROR;
};
dummyStringFetch:
PROC =
TRUSTED {
str: LONG STRING ← head;
index: CARDINAL ← start; -- to allow for narrowing
c ← 0C};
innerStringFetch:
PROC =
TRUSTED {
c ← head[start];
};
innerRopeInlineFetch:
PROC =
TRUSTED {
c ← Rope.InlineFetch[header, start];
};
innerRopeFetch:
PROC =
TRUSTED {
c ← Rope.Fetch[header, start];
};
innerRopeLength:
PROC =
TRUSTED {
[] ← Rope.Length[header];
};
innerNew:
PROC =
TRUSTED {
dummyREF ← NEW[Dummy];
};
innerDoublePointerAssign:
PROC =
TRUSTED {
dummyPtr ← dummyPtr1;
dummyPtr ← NIL;
};
innerDoubleRefAssign:
PROC =
TRUSTED {
dummyREF ← dummyREF1;
dummyREF ← NIL;
};
innerAppendString:
PROC =
TRUSTED {
str.length ← 2;
AppendString[str, "bb"];
};
innerRopeConcat:
PROC =
TRUSTED {
[] ← Rope.Concat["aa", "bb"];
};
dummyCardinalAdd:
PROC =
TRUSTED {
card: CARDINAL ← cardinal;
card ← cardinal;
};
innerCardinalAdd:
PROC =
TRUSTED {
card: CARDINAL ← cardinal;
card ← card + cardinal;
};
innerCardinalMult:
PROC =
TRUSTED {
card: CARDINAL ← cardinal;
card ← card * cardinal;
};
innerCardinalDiv:
PROC =
TRUSTED {
card: CARDINAL ← cardinal;
card ← cardinal / 100;
};
dummyIntAdd:
PROC =
TRUSTED {
li: INT ← int;
li ← int;
};
innerIntAdd:
PROC =
TRUSTED {
li: INT ← int;
li ← li + int;
};
innerIntMult:
PROC =
TRUSTED {
li: INT ← int;
li ← li * int;
};
innerIntDiv:
PROC =
TRUSTED {
li: INT ← int;
li ← li / 100;
};
dummyRealAdd:
PROC =
TRUSTED {
r: REAL ← pi;
r ← pi;
};
innerRealAdd:
PROC =
TRUSTED {
r: REAL;
r ← pi + pi;
};
innerRealMult:
PROC =
TRUSTED {
r: REAL;
r ← pi * pi;
};
innerRealDiv:
PROC =
TRUSTED {
r: REAL;
r ← pi / 100.0;
};
innerNullProc:
PROC =
TRUSTED {
NullProc[];
};
innerNullProcVar:
PROC =
TRUSTED {
nullProc[];
};
innerNullEntryProc:
PROC =
TRUSTED {
NullEntryProc[];
};
innerNullEntryProcVar:
PROC =
TRUSTED {
nullEntryProc[];
};
innerProcAndCatch:
PROC =
TRUSTED {
CauseError[! MyError => CONTINUE];
};
innerRandom:
PROC =
TRUSTED {
[] ← Random.Choose[0, 1000];
};
innerWriteToFile:
PROC =
TRUSTED {
st: IO.STREAM ← FS.StreamOpen["GoodTimes.test", create];
FOR i:
NAT
IN [0..1000)
DO
j: CARDINAL ← i MOD headSize;
IO.PutChar[st, 15C];
ENDLOOP;
IO.Close[st];
};
run some timing tests
Report[innerBool1, "{b: BOOL ← TRUE; IF b THEN {b ← TRUE}}",
1000, dummyProc0];
Report[innerBool2, "{b: BOOL ← FALSE; IF b THEN {b ← TRUE}}",
1000, dummyProc0];
Report[innerNarrow, "NARROW", 1000];
Report[innerRefSelect1, "REF SELECT", 1000];
Report[innerRefSelect2, "REF SELECT (2nd case)", 1000];
Report[innerRefSelect3, "REF SELECT (3rd case)", 1000];
Report[innerVariantSelect, "variant record SELECT", 1000];
Report[innerStringFetch, "LONG STRING fetch", 1000, dummyStringFetch];
Report[innerRopeInlineFetch, "Rope.InlineFetch (flat rope)", 1000, dummyStringFetch];
Report[innerRopeFetch, "Rope.Fetch (flat rope)", 1000, dummyStringFetch];
Report[innerRopeLength, "Rope.Length (flat rope)", 1000];
Report[innerNew, "NEW (4-word object)"];
Report[innerDoublePointerAssign, "double long pointer assign (g1 ← g2; g1 ← NIL)",
10000, dummyProc0];
Report[innerDoubleRefAssign, "double AssignRefs (g1 ← g2; g1 ← NIL)",
1000, dummyProc0];
Report[innerAppendString, "AppendString[str, 'bb'] (str = 'aa')"];
Report[innerRopeConcat, "Rope.Concat['aa', 'bb']"];
Report[innerCardinalAdd, "CARDINAL add (card ← 31416 + 31416)",
10000, dummyCardinalAdd];
Report[innerCardinalMult, "CARDINAL multiply (card ← 31416 * 31416)",
10000, dummyCardinalAdd];
Report[innerCardinalDiv, "CARDINAL divide (card ← 31416 / 100)",
10000, dummyCardinalAdd];
Report[innerIntAdd, "INT add (li ← 31416 + 31416)", 1000, dummyIntAdd];
Report[innerIntMult, "INT multiply (li ← 31416 * 31416)", 1000, dummyIntAdd];
Report[innerIntDiv, "INT divide (li ← 31416 / 100)", 1000, dummyIntAdd];
Report[innerRealAdd, "REAL add (li ← 3.1416 + 3.1416)", 1000, dummyRealAdd];
Report[innerRealMult, "REAL multiply (li ← 3.1416 * 3.1416)", 1000, dummyRealAdd];
Report[innerRealDiv, "REAL divide (li ← 3.1416 / 100.0)", 1000, dummyRealAdd];
Report[innerNullProc, "local proc call", 1000, dummyProc0];
Report[innerNullProcVar, "local proc var call", 1000, dummyProc0];
Report[innerNullEntryProc, "local ENTRY proc call", 1000, dummyProc0];
Report[innerNullEntryProcVar, "local ENTRY proc var call", 1000, dummyProc0];
Report[innerProcAndCatch, "proc call & caught ERROR", 100, dummyProc0];
Report[innerRandom, "[] ← Random.Choose[0, 1000]", 100, dummyProc0];
Report[innerWriteToFile, "for FileIO.Open, 1000 IO.PutChars, IO.Close", 10];
WriteRope["Checking consistency of Rope operations... "];
{
-- perform some random consistency checks on ropes
last: ROPE ← NIL;
localMark: Microseconds ← GetMark[];
offset ← 0;
FOR i:
NAT
IN [0..100)
DO
ref: ROPE ← RandomRope[100, 40];
size: INT ← ref.Size[];
start: INT ← Random.Choose[0,size];
len: INT ← Random.Choose[0,size];
rep: ROPE ← Rope.Substr[ref, Random.Choose[0,size], Random.Choose[0,size]];
ref ← Rope.Replace[ref, start, len, rep];
CheckReplace[ref, start, len, rep];
last ← ref;
ENDLOOP;
ItsShowTime[localMark, "to check."];
};
ShowGC[];
now try to exercise ropes and the allocator
IF showRopes
THEN {
WritePageMark[];
FOR i:
CARDINAL
IN [0..4)
DO
ShowNumber["Round #", i];
{base:
ROPE ← Rope.Concat[header, MakeFill['$, 100]];
replSize: CARDINAL ← Random.Choose[1, 6];
rep: ROPE ← MakeFill['*, replSize];
subMark: Microseconds ← GetMark[];
size: INT ← base.Size[];
FOR j:
CARDINAL
IN [0..10)
DO
start ← Random.Choose[0, size];
FOR k:
CARDINAL
IN [0..100)
DO
base ← base.Replace[start, 0, rep];
size ← size + replSize;
start ← start + replSize;
ENDLOOP;
ENDLOOP;
ItsShowTime[subMark, "for 1000 insertions", ", "];
{leaves,nodes,maxDepth:
INT ← 0;
[leaves,nodes,maxDepth] ← base.VerifyStructure[];
ShowNumber[" size: ", size];
ShowNumber[", leaves: ", leaves];
ShowNumber[", nodes: ", nodes];
ShowNumber[", depth: ", maxDepth];
{mapTime: Microseconds ← GetMark[];
[] ← Rope.Map[base, 0, Rope.MaxLen, NullAction];
ItsShowTime[mapTime, "to map", "; "];
mapTime ← GetMark[];
base ← base.Balance[];
ItsShowTime[mapTime, "to balance", " ", FALSE];
[leaves,nodes,maxDepth] ← base.VerifyStructure[];
IF size # base.Size[] THEN ERROR; -- Balance failed!
ShowNumber[", leaves: ", leaves];
ShowNumber[", nodes: ", nodes];
ShowNumber[", depth: ", maxDepth];
WriteChar[15C]}}};
ENDLOOP;
ShowGC[];
};
IF swapTraceAndSweep THEN useTraceAndSweep ← NOT useTraceAndSweep;
IF pause >= 0 THEN Process.Pause[Process.MsecToTicks[pause]];
};
Commander.Register["GoodTimes", GoodTimesCommand, "Some simple timing tests."];