{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}