MODULE Performance;
IMPORT KernelLog, Machine, Modules, Objects, Kernel, Random, Math;
CONST
LoadPeriod = 5;
IdlePeriod = 10;
Time0 = 1; Time1 = 5; Time2 = 15;
Trace = FALSE;
TYPE
Monitor = OBJECT
VAR
period: LONGINT; rand: Random.Generator; timer: Kernel.Timer;
loops: LONGINT; state: SHORTINT;
PROCEDURE &Init*(period: LONGINT; randomized: BOOLEAN);
BEGIN
SELF.period := period; state := 0;
IF randomized THEN NEW(rand) ELSE rand := NIL END
END Init;
PROCEDURE Compute(loops: LONGINT);
END Compute;
PROCEDURE Stop;
BEGIN {EXCLUSIVE}
IF state = 0 THEN INC(state) END;
timer.Wakeup;
AWAIT(state = 2)
END Stop;
BEGIN {ACTIVE, PRIORITY(Objects.High)}
NEW(timer); loops := 0;
WHILE state = 0 DO
INC(loops); Compute(loops);
IF rand # NIL THEN
timer.Sleep(ENTIER(period*(1000*2)*rand.Uniform()))
ELSE
timer.Sleep(period*1000)
END
END;
BEGIN {EXCLUSIVE} INC(state) END
END Monitor;
TYPE
LoadMonitor = OBJECT (Monitor)
VAR exp: ARRAY 3 OF REAL;
PROCEDURE &Init*(period: LONGINT; randomized: BOOLEAN);
BEGIN
Init^(period, randomized);
load[0] := 0; load[1] := 0; load[2] := 0;
exp[0] := 1/Math.exp(period/(Time0*60));
exp[1] := 1/Math.exp(period/(Time1*60));
exp[2] := 1/Math.exp(period/(Time2*60))
END Init;
PROCEDURE Compute(loops: LONGINT);
VAR n: LONGINT;
BEGIN
n := Objects.NumReady() - 1;
UpdateLoad(load[0], exp[0], n);
UpdateLoad(load[1], exp[1], n);
UpdateLoad(load[2], exp[2], n);
IF Trace THEN
KernelLog.Int(loops, 8); KernelLog.Int(n, 3);
WriteLoad(load[0]); WriteLoad(load[1]); WriteLoad(load[2]);
KernelLog.Ln
END
END Compute;
END LoadMonitor;
TYPE
IdleMonitor = OBJECT (Monitor)
VAR milliTimer: Kernel.MilliTimer; idlecount: ARRAY Machine.MaxCPU OF LONGINT;
PROCEDURE &Init*(period: LONGINT; randomized: BOOLEAN);
VAR i: LONGINT;
BEGIN
Init^(period, randomized);
FOR i := 0 TO Machine.MaxCPU-1 DO
idlecount[i] := Objects.idlecount[i]; idle[i] := -1
END;
Kernel.SetTimer(milliTimer, 0);
END Init;
PROCEDURE Compute(loops: LONGINT);
VAR i, ic, id, td: LONGINT;
BEGIN
td := Kernel.Elapsed(milliTimer); Kernel.SetTimer(milliTimer, 0);
IF td = 0 THEN td := 1 END;
FOR i := 0 TO Machine.MaxCPU-1 DO
ic := Objects.idlecount[i];
id := ic - idlecount[i]; idlecount[i] := ic;
IF ic # 0 THEN
idle[i] := id*100 DIV td
END
END;
IF Trace THEN
KernelLog.Int(loops, 8);
FOR i := 0 TO Machine.MaxCPU-1 DO KernelLog.Int(idle[i], 5) END;
KernelLog.Ln
END
END Compute;
END IdleMonitor;
VAR
load*: ARRAY 3 OF REAL;
idle*: ARRAY Machine.MaxCPU OF LONGINT;
loadmon: LoadMonitor;
idlemon: IdleMonitor;
PROCEDURE UpdateLoad(VAR load: REAL; exp: REAL; n: LONGINT);
BEGIN
load := load*exp + (1-exp)*n
END UpdateLoad;
PROCEDURE WriteLoad(load: REAL);
VAR x: LONGINT;
BEGIN
IF Trace THEN
x := ENTIER(load*100 + 0.5);
KernelLog.Int(x DIV 100, 3); KernelLog.Char(".");
KernelLog.Int(x DIV 10 MOD 10, 1); KernelLog.Int(x MOD 10, 1)
END
END WriteLoad;
PROCEDURE Cleanup;
BEGIN
IF loadmon # NIL THEN loadmon.Stop; loadmon := NIL END;
IF idlemon # NIL THEN idlemon.Stop; idlemon := NIL END
END Cleanup;
BEGIN
Modules.InstallTermHandler(Cleanup);
IF loadmon = NIL THEN NEW(loadmon, LoadPeriod, TRUE) END;
IF idlemon = NIL THEN NEW(idlemon, IdlePeriod, FALSE) END
END Performance.
(**
Notes:
o "load" is a Unix-like estimate of the average number of ready and running processes over the past 1, 5 and 15 minutes.
o "idle" is an estimate of the percentage of idle time per processor over the last 10 seconds.
o When a processor is not available, its idle estimate is -1.
*)
(*
to do:
o fix idle on single-processor
o adjust idle computation to timeslice rate (currently assumes timeslice rate = AosTimer rate)
o Kernel.GetTimer and Objects.idlecount effects can give 101% idle
*)
System.Free Performance ~
System.ShowCommands Performance
System.State Performance