{Begin SubSec Performance Measuring Functions}
{Title Performance Measuring Functions}
{Text


{FnDef {FnName CONSCOUNT} {FnArgs N}
{Text
{lisp (CONSCOUNT)} returns the number of {fn CONS}es since Interlisp started up.  If {arg N} is not {lisp NIL}, resets {fn CONSCOUNT} to {arg N}.
}}



{FnDef {FnName BOXCOUNT} {FnArgs TYPE N}
{Text
Returns the number of boxing operations for the data type {arg TYPE} (see {PageRef Tag BOXING}) since Interlisp started up.  If {arg N} is not {lisp NIL}, the corresponding counter is reset to {arg N}.

In Interlisp-10, if {arg TYPE}={lisp NIL}, {fn BOXCOUNT} returns the number of large integer boxes; if {arg TYPE} is non-{lisp NIL}, it returns the number of floating boxes.  These counters are directly accessible via the {lisp COREVAL}s {lisp IBOXCN} and {lisp FBOXCN}.    

In Interlisp-D, {arg TYPE} can be any datatype name, in addition to {lisp FIXP} and {lisp FLOATP}.
}}



{FnDef {FnName PAGEFAULTS} {FnArgs }
{Text
Returns the number of page faults since Interlisp started up.
}}




{FnDef {FnName TIME} {FnArgs TIMEX TIMEN TIMETYPE}
{Type NLAMBDA}
{Text
An nlambda function.  It executes the computation {arg TIMEX}, and prints out the number of conses and computation time.  Garbage collection time is subtracted out.  For example, in Interlisp-10:

{lispcode
←TIME((LOAD (QUOTE PRETTY) (QUOTE PROP]
FILE CREATED 1-AUG-78 14:56:12
PRETTYCOMS
collecting lists
582, 10291 free cells
13169 CONSES
29.484 SECONDS
PRETTY}

If {arg TIMEN} is greater than 1 ({arg TIMEN}={lisp NIL} is equivalent to
{arg TIMEN}=1), {arg TIMEX} is executed {arg TIMEN} times, and {fn TIME} prints out (number of conses)/{arg TIMEN}, and (computation time)/{arg TIMEN}.  This is useful for more accurate measurement on small computations, e.g.

{lispcode
←TIME((COPY (QUOTE (A B C))) 10)
30/10 = 3 CONSES
.055/10 = .0055 SECONDS
(A B C)}

If {arg TIMETYPE} is 0, {fn TIME} measures and prints total {it real} time as well as computation time, e.g.

{lispcode
←TIME((LOAD (QUOTE PRETTY) (QUOTE PROP)) 1 0]
FILE CREATED 7-MAY-71 12:47:14
GC: 8
582, 10291 FREE WORDS
PRETTYFNS
PRETTYVARS
3727 CONSES
11.193 SECONDS
27.378 SECONDS, REAL TIME
PRETTY}

If {arg TIMETYPE} = 3, {index TIME FN}{fn TIME} measures and
prints garbage collection time as well as computation time, e.g.

{lispcode
←TIME((LOAD (QUOTE PRETTY) (QUOTE PROP)) 1 3]
FILE CREATED 7-MAY-71 12:47:14
GC: 8
582, 1091 FREE WORDS
PRETTYFNS
PRETTYVARS
3727 CONSES
10.597 SECONDS
1.487 SECONDS, GARBAGE COLLECTION TIME
PRETTY}

Another option is {arg TIMETYPE}={lisp T}, in which case
{fn TIME} measures and prints the number of pagefaults.

The value of {index TIME FN}{fn TIME} is the value of the
last evaluation of {arg TIMEX}.
}}




{Begin SubSec BREAKDOWN}
{Title BREAKDOWN}
{Text

{note {fn BREAKDOWN} was written by W. Teitelman, and extended by L. P. Deutsch.}

{index *BEGIN* BREAKDOWN FN}

{fn TIME} collects statistics for whole computations.  {fn BREAKDOWN} is available to analyze the breakdown of computation time (or any other measureable quantity) function by function.


{FnDef {Name BREAKDOWN} {Args FN{sub 1} {ellipsis} FN{sub N}}
{Type NLAMBDA NOSPREAD}
{Text
The user calls {fn BREAKDOWN} giving it a list of function names (unevaluated).  These functions are modified so that they keep track of various statistics.

To remove functions from those being monitored, simply {index UNBREAK FN}{fn UNBREAK} ({PageRef Fn UNBREAK}) the functions, thereby restoring them to their original state.  To add functions, call {fn BREAKDOWN} on the new functions.  This will not reset the counters for any functions not on the new list.  However {lisp (BREAKDOWN)} will zero the counters of all functions being monitored.

The procedure used for measuring is such that if one function calls other and both are "broken down", then the time (or whatever quantity is being measured) spent in the inner function is {it not} charged to the outer function as well.  

Note:  {fn BREAKDOWN} will {it not} give accurate results if a function being measured is not returned from normally, e.g., a lower {fn RETFROM} (or {fn ERROR}) bypasses it.  In this case, all of the time (or whatever quantity is being measured) between the time that function is entered and the time the next function being measured is entered will be charged to the first function.
}}


{FnDef {Name BRKDWNRESULTS} {Args RETURNVALUESFLG}
{Text
{fn BRKDWNRESULTS} prints the analysis of the statistics requested as well as the number of calls to each function.  If {arg RETURNVALUESFLG} is non-{lisp NIL}, {fn BRKDWNRESULTS} will not to print the results, but instead return them in the form of a list of elements of the form {lisp ({arg FNNAME} {arg #CALLS} {arg VALUE})}.
}}



Example:

{lispcode
← (BREAKDOWN SUPERPRINT SUBPRINT COMMENT1)
(SUPERPRINT SUBPRINT COMMENT1)
← (PRETTYDEF '(SUPERPRINT) 'FOO)
FOO.;3
← (BRKDWNRESULTS)
FUNCTIONS     TIME      #CALLS   PER CALL     %
SUPERPRINT    8.261       365     0.023      20
SUBPRINT      31.910      141     0.226      76
COMMENT1      1.612       8       0.201      4
TOTAL         41.783      514     0.081
NIL
← (BRKDWNRESULTS T)
((SUPERPRINT 365 8261) (SUBPRINT 141 31910) (COMMENT1 8 1612))}



{fn BREAKDOWN} can be used to measure other statistics, by setting the following variables:


{VarDef {Name BRKDWNTYPE}
{Text
To use {fn BREAKDOWN} to measure other statistics, before calling {fn BREAKDOWN}, set the variable {var BRKDWNTYPE} to the quantity of interest, e.g., {lisp TIME}, {lisp CONSES}, etc, or a list of such quantities.  Whenever {fn BREAKDOWN} is called with {var BRKDWNTYPE} not {lisp NIL}, {fn BREAKDOWN} performs the necessary changes to its internal state to conform to the new analysis.  In particular, if this is the first time an analysis is being run with a particular statistic, a measuring function will be defined, and the compiler will be called to compile it.  The functions being broken down will be redefined to call this measuring function.  When {fn BREAKDOWN} is through initializing, it sets {var BRKDWNTYPE} back to {lisp NIL}.  Subsequent calls to {fn BREAKDOWN} will measure the new statistic until {var BRKDWNTYPE} is again set and a new {fn BREAKDOWN} performed.
}}


{VarDef {Name BRKDWNTYPES}
{Text
The list {var BRKDWNTYPES} contains the information used to analyze new statistics.  Each entry on {var BRKDWNTYPES} should be of the form {lisp ({arg TYPE} {arg FORM} {arg FUNCTION})}, where {arg TYPE} is a statistic name (as would appear in {var BRKDWNTYPE}), {arg FORM} computes the statistic, and {arg FUNCTION} (optional) converts the value of form to some more interesting quantity.  For example, {lisp (TIME (CLOCK 2) (LAMBDA (X) (FQUOTIENT X 1000)))} measures computation time and reports the result in seconds instead of milliseconds.  {var BRKDWNTYPES} currently contains entries for {lisp TIME}, {lisp CONSES}, {lisp PAGEFAULTS}, {lisp BOXES}, and {lisp FBOXES}.
}}



Example:


{lispcode
← (SETQ BRKDWNTYPE '(TIME CONSES))
(TIME CONSES)
← (BREAKDOWN MATCH CONSTRUCT)
(MATCH CONSTRUCT)
← (FLIP '(A B C D E F G H C Z) '(.. $1 .. #2 ..) '(.. #3 ..))
(A B D E F G H Z)
← (BRKDWNRESULTS)
FUNCTIONS     TIME       #CALLS   PER CALL    %
MATCH         0.036       1        0.036     54
CONSTRUCT     0.031       1        0.031     46
TOTAL         0.067       2        0.033 
FUNCTIONS     CONSES    #CALLS   PER CALL     %
MATCH         32          1       32.000     40
CONSTRUCT     49          1       49.000     60
TOTAL         81          2       40.500 
NIL}



Occasionally, a function being analyzed is sufficiently fast that the overhead involved in measuring it obscures the actual time spent in the function.
If the user were using {fn TIME}, he would specify a value for {arg TIMEN} greater than 1 to give greater accuracy.  A similar option is available for {fn BREAKDOWN}.  The user can specify that a function(s) be executed a multiple number of times for each measurement, and the average value reported, by including a number in the list of functions given to {fn BREAKDOWN}, e.g., {lisp BREAKDOWN(EDITCOM EDIT4F 10 EDIT4E EQP)} means normal breakdown for {lisp EDITCOM} and {lisp EDIT4F} but executes (the body of) {lisp EDIT4E} and {lisp EQP} 10 times each time they are called.  Of course, the functions so measured must not cause any harmful side effects, since they are executed more than once for each call.  The printout from {fn BRKDWNRESULTS} will look the same as though each function were run only once, except that the measurement will be more accurate.


Another way of obtaining more accurate measurement is to expand the call to the measuring function in-line.  If the value of {var BRKDWNCOMPFLG}{index BRKDWNCOMPFLG Var} is non-{lisp NIL} (initially {lisp NIL}), then whenever a function is broken-down, it will be redefined to call the measuring function, and then recompiled.  The measuring function is expanded in-line via an appropriate macro.  In addition, whenever {var BRKDWNTYPE}{index BRKDWNTYPE Var} is reset, the compiler is called for {it all} functions for which {var BRKDWNCOMPFLG} was set at the time they were originally broken-down, i.e. the setting of the flag at the time a function is broken-down determines whether the call to the measuring code is compiled in-line.



{Begin Note}
Date:  1 MAY 1977 0914-PDT
From: DEUTSCH
Subject: BREAKDOWN

Yes, I'm willing to negotiate.  I'll specify the data structures later, but here is the interface I have in mind:
1) To specify measuring several things at once, simply set BRKDWNTYPE to a list, e.g. (TIME CONSES).  I haven't yet figured out a nice format for printing out the results if they won't fit on one line -- maybe you have some suggestions for that.
2) To specify open compilation of the BRKDWN2 form, set BREAKDOWNCOMPFLG to T (initially NIL).  As long as this flag is T, any newly BREAKDOWN'ed functions, or ALL functions if you change BRKDWNTYPE, will use open code for BRKDWN2 (less time overhead, more space).  Any mix of open and closed code is permissible, i.e. things will be OK no matter or how often you change the flag setting.  I think it might also be worth indicating on the RESULTS printout which functions use open code.
{End Note}

{Begin Note}
Date:  1 MAY 1977 1556-PDT
From: DEUTSCH
Subject: printing multiple stats

As a first cut, I am just producing a separate listing in the current format for each statistic being measured, e.g.
FNS	TIME	# CALLS	PER CALL	%
...

FNS	CONSES	# CALLS	PER CALL	%
...

I am not wildly happy about this, but it will do until we can think of something better.
{End Note}

{Begin Note}
Date: 31 MAY 1977 2346-PDT
From: DEUTSCH
Subject: New BRKDWN

I can have it ready for you tomorrow if you want it.

The new capabilities are all backward-compatible.  THe changes are as follows:
1) BRKDWNTYPE can be a list of types, all those things will be measured.
2) If BRKDWNCOMPFLG is non-NIL, the BRKDWN2's are expanded in-line, leading to much more precise measurements at a substantial cost in code space, plus considerably longer setup time (for some reason the compiler seems to be very slow, even on fairly simple functions).

If you and/or Mitch want to do someting about a bargraph display, I will change RESULTS so you can get it to deliver the results in a nice list form rather than printing them out.
{End Note}

{Begin Note}
Date:  1 JUN 1977 1451-PDT
From: DEUTSCH
Subject: BRKDWN

You may take the version from <DEUTSCH> on Maxc2.  It works as far as I can tell.  It includes multiple measurements and BRKDWNCOMPFLG.

I don't think a name like RESULTS belongs in a package like BREAKDOWN, so I renamed the function that does the work as BRKDWNRESULTS and just made RESULTS call it with no arguments.  My feeling is that extensions to result reporting, like you and Mitch might want, should go in BRKDWNRESULTS.

I've implemented one such extension: (BRKDWNRESULTS T) returns the results in a list whose elements are of the form (function ncalls stat1 ... statn) where the stat1 ... statn are raw data, divided by the repetition counts if any but not by the number of calls, and in the order given by BRKDWNLABELS (a copy of the original BRKDWNTYPE). The intention is that later you could give BRKDWNRESULTS a list to smash the results into, e.g. if you only wanted them for one function you would give it a list of one such element.  I'm not sure this is the best way to go, so you might consider not publicizing it.
{End Note}


{Begin Note}
Date: 30 Aug. 1982 4:44 pm PDT (Monday)
From: Raim.EOS
Subject: BREAKDOWN

Because the Dolphin CPU "runs" during keyboard IO waits, a large bundle of CPU time get ascribed to interactive functions by BREAKDOWN thereby obscuring the real costs.

One user, Darrel Van Buer, has patched the problem by including \BACKGROUND in the functions being monitored and then manually ignoring that entry.  Should other functions be similarly treated?  Can BREAKDOWN be changed to make this a permanent feature to make timing more comparable with the 10 version?
{End Note}


{index *END* BREAKDOWN FN}

}{End SubSec BREAKDOWN}


}{End SubSec Performance Measuring Functions}