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;
};
END.