CodeTimerImpl.mesa
Copyright © 1986 by Xerox Corporation. All rights reserved.
Last edited by Bier on June 18, 1986
Contents: Routines for maintaining a table of average times for user-specified operations.
DIRECTORY
Atom, BasicTime, CodeTimer, IO, Rope;
CodeTimerImpl: CEDAR PROGRAM
IMPORTS Atom, BasicTime, IO
EXPORTS CodeTimer = BEGIN
Table: TYPE = REF TableObj;
TableObj: TYPE = CodeTimer.TableObj;
Interval: TYPE = REF IntervalObj;
IntervalObj: TYPE = CodeTimer.IntervalObj;
gTableList: LIST OF Table;
Problem: PUBLIC SIGNAL [msg: Rope.ROPE] = CODE;
CreateTable: PUBLIC PROC [name: ATOM] RETURNS [table: Table] = {
oldTable: Table;
table ← NEW[TableObj ← [NIL, name]];
oldTable ← GetTable[name];
IF oldTable = NIL THEN gTableList ← CONS[table, gTableList]
ELSE oldTable.intervals ← NIL;
};
Getting ready to test performance.
GetTable: PUBLIC PROC [name: ATOM] RETURNS [table: Table] = {
FOR list: LIST OF Table ← gTableList, list.rest UNTIL list = NIL DO
IF list.first.name = name THEN RETURN[list.first];
ENDLOOP;
RETURN[NIL];
};
ResetTable: PUBLIC PROC [table: Table] = {
FOR list: LIST OF Interval ← table.intervals, list.rest UNTIL list = NIL DO
ResetIntervalInternal[list.first];
ENDLOOP;
};
CreateInterval: PUBLIC PROC [name: ATOM, subintervals: LIST OF Interval ← NIL] RETURNS [interval: Interval] = {
interval ← NEW[IntervalObj ← [name: name, subintervals: subintervals, starts: 0, stops: 0, unmatchedStarts: 0, startTime: 0, totalTime: 0, maxTime: 0, maxIndex: 9999, minTime: LAST[LONG CARDINAL]]];
};
ResetIntervalInternal: PROC [interval: Interval] = {
FOR list: LIST OF Interval ← interval.subintervals, list.rest UNTIL list = NIL DO
ResetIntervalInternal[list.first];
ENDLOOP;
interval.starts ← 0;
interval.stops ← 0;
interval.unmatchedStarts ← 0;
interval.startTime ← 0;
interval.totalTime ← 0;
interval.maxTime ← 0;
interval.minTime ← LAST[LONG CARDINAL]
};
AddInterval: PUBLIC PROC [interval: Interval, table: Table] = {
table.intervals ← CONS[interval, table.intervals];
};
AddInt: PUBLIC PROC [interval: Interval, tableName: ATOM] = {
AddInterval[interval, GetTable[tableName]];
};
FindInterval: PROC [name: ATOM, table: Table] RETURNS [Interval] = {
sub: Interval;
success: BOOL;
FOR list: LIST OF Interval ← table.intervals, list.rest UNTIL list = NIL DO
[sub, success] ← FindSubInterval[name, list.first];
IF success THEN RETURN[sub];
ENDLOOP;
SIGNAL Problem[msg: "No such interval in this table."];
RETURN[NIL];
};
FindSubInterval: PROC [name: ATOM, interval: Interval] RETURNS [sub: Interval, success: BOOL] = {
IF interval.name = name THEN RETURN[interval, TRUE];
FOR subList: LIST OF Interval ← interval.subintervals, subList.rest UNTIL subList = NIL DO
[sub, success] ← FindSubInterval[name, subList.first];
IF success THEN RETURN;
ENDLOOP;
RETURN[NIL, FALSE];
};
Testing performance.
StartInterval: PUBLIC PROC [name: ATOM, table: Table] = {
interval: Interval ← FindInterval[name, table];
IF interval.starts >= interval.stops + 1 THEN {
interval.starts ← interval.stops;
interval.unmatchedStarts ← interval.unmatchedStarts + 1;
};
interval.starts ← interval.starts + 1;
interval.startTime ← BasicTime.GetClockPulses[];
};
StartInt: PUBLIC PROC [intervalName: ATOM, tableName: ATOM] = {
StartInterval[intervalName, GetTable[tableName]];
};
StopInterval: PUBLIC PROC [name: ATOM, table: Table] = {
interval: Interval;
stopTime, elapsedTime: BasicTime.Pulses;
stopTime ← BasicTime.GetClockPulses[];
interval ← FindInterval[name, table];
interval.stops ← interval.stops + 1;
elapsedTime ← stopTime - interval.startTime;
interval.totalTime ← interval.totalTime + elapsedTime;
interval.minTime ← IF elapsedTime < interval.minTime THEN elapsedTime ELSE interval.minTime;
IF elapsedTime > interval.maxTime THEN {
interval.maxTime ← elapsedTime;
interval.maxIndex ← interval.starts;
};
};
StopInt: PUBLIC PROC [intervalName: ATOM, tableName: ATOM] = {
StopInterval[intervalName, GetTable[tableName]];
};
Printing results.
PrintTable: PUBLIC PROC [f: IO.STREAM, table: Table] = {
FOR list: LIST OF Interval ← table.intervals, list.rest UNTIL list = NIL DO
PrintInterval[f, list.first, 0];
ENDLOOP;
};
PrintInterval: PUBLIC PROC [f: IO.STREAM, interval: Interval, nestingLevel: NAT ← 0] = {
name: Rope.ROPE;
totalTime, avgTime, minTime, maxTime: LONG CARDINAL;
IF interval.starts # 0 THEN {
FOR i: NAT IN [1..nestingLevel] DO
f.PutChar[IO.TAB];
ENDLOOP;
name ← Atom.GetPName[interval.name];
totalTime ← BasicTime.PulsesToMicroseconds[interval.totalTime]/1000;
f.PutF["%g. starts: %g. total: %g. ", [rope[name]], [integer[interval.starts]], [integer[totalTime]]];
avgTime ← totalTime/interval.starts;
minTime ← BasicTime.PulsesToMicroseconds[interval.minTime]/1000;
maxTime ← BasicTime.PulsesToMicroseconds[interval.maxTime]/1000;
f.PutF["avg: %g. min: %g. max: %g, index: %g, overflows: %g\n", [integer[avgTime]], [integer[minTime]], [integer[maxTime]], [integer[interval.maxIndex]], [integer[interval.unmatchedStarts]]];
FOR children: LIST OF Interval ← interval.subintervals, children.rest UNTIL children = NIL DO
PrintInterval[f, children.first, nestingLevel+1];
ENDLOOP;
};
};
PrintInt: PUBLIC PROC [f: IO.STREAM, intervalName: ATOM, tableName: ATOM, nestingLevel: NAT ← 0] = {
table: Table ← GetTable[tableName];
interval: Interval ← FindInterval[intervalName, table];
PrintInterval[f, interval, nestingLevel];
};
IntervalStatistics: TYPE = REF IntervalStatisticsObj;
IntervalStatisticsObj: TYPE = CodeTimer.IntervalStatisticsObj;
GetIntervalStats: PUBLIC PROC [interval: Interval] RETURNS [starts: CARD, totalMsec: CARD, averageMsec: CARD, minMsec: CARD, maxMsec: CARD, worstInterval: CARD, startsWithoutStops: CARD, subIntervals: LIST OF IntervalStatistics] = {
Like PrintInterval but returns the values for use by a client instead of writing them to a stream.
ptr: LIST OF IntervalStatistics;
child: IntervalStatistics;
starts ← interval.starts;
totalMsec ← BasicTime.PulsesToMicroseconds[interval.totalTime]/1000;
averageMsec ← totalMsec/interval.starts;
minMsec ← BasicTime.PulsesToMicroseconds[interval.minTime]/1000;
maxMsec ← BasicTime.PulsesToMicroseconds[interval.maxTime]/1000;
worstInterval ← interval.maxIndex;
startsWithoutStops ← interval.unmatchedStarts;
[subIntervals, ptr] ← StartIntervalStatisticsList[];
FOR children: LIST OF Interval ← interval.subintervals, children.rest UNTIL children = NIL DO
child ← NEW[IntervalStatisticsObj];
child.name ← children.first.name;
[child.starts, child.totalMsec, child.averageMsec, child.minMsec, child.maxMsec, child.worstInterval, child.startsWithoutStops,----] ← GetIntervalStats[children.first];
[subIntervals, ptr] ← AddIntervalStatistics[child, subIntervals, ptr];
ENDLOOP;
};
GetIntStats: PUBLIC PROC [intervalName: ATOM, tableName: ATOM] RETURNS [starts: CARD, totalMsec: CARD, averageMsec: CARD, minMsec: CARD, maxMsec: CARD, worstInterval: CARD, startsWithoutStops: CARD, subIntervals: LIST OF IntervalStatistics] = {
Like PrintInt but returns the values for use by a client instead of writing them to a stream.
table: Table ← GetTable[tableName];
interval: Interval ← FindInterval[intervalName, table];
RETURN GetIntervalStats[interval];
};
StartIntervalStatisticsList: PUBLIC PROC [] RETURNS [entityList, ptr: LIST OF IntervalStatistics] = {
ptr ← entityList ← NIL;
};
AddIntervalStatistics: PUBLIC PROC [entity: IntervalStatistics, entityList, ptr: LIST OF IntervalStatistics] RETURNS [newList, newPtr: LIST OF IntervalStatistics] = {
IF ptr = NIL THEN {
IF NOT entityList = NIL THEN ERROR;
newPtr ← newList ← CONS[entity, NIL];
RETURN;
}
ELSE {
newList ← entityList;
ptr.rest ← CONS[entity, NIL];
newPtr ← ptr.rest;
};
};
Init: PROC [] = {
gTableList ← NIL;
};
Init[];
END.