report ← {larg} (dfn ##.mdf) rarg           ⍝ Monitor D function.

Mdf applies its operand D function to (or between) its argument(s) then displays
the  function  with  a count of the number of times each line was executed. This
output  is  a useful indication of the "hot spots" in the subject function where
it might pay to review the code in order to improve overall performance.

NB: From dyalog V13, ⎕PROFILE will supply this information directly.        <V>

Technical notes:

The  coding of this operator is "nasty" in the sense that there is extensive in-
terference between the operator and its subject D function. In particular, there
is scope for name clashes, which with luck, have been avoided by choosing unlik-
ely names, and refraining from naming transient values where possible.

The  operator  initialises  a  count vector "_mdf_c" with zeros corresponding to
each  line  of  the subject function. It then makes a temporary copy "_mdf_f" of
the subject function, prefacing each line with a segment of code that increments
the count for this line:

    _mdf_c[⎕io+⍬⍴⎕lc]+←1 ⋄

Further nastiness occurs in order to determine the name of the subject function.
The approach taken is to compare all but the header source line of the temporary
function  with  those of all functions in the current namespace.

From Dyalog V13, this operator can be coded in a more pleasant way:         <V>

    mdf←{                                       ⍝ Line hits for function ⍺⍺.
        name←⍺⍺¨{ff←⍺⍺ ⋄ (⍕⎕OR'ff')~'∇ ¨'}⍬     ⍝ deduce name of operand fn.
        _←⎕PROFILE¨'reset' 'on'                 ⍝ initialise profilng.
        ⍺←{⍵} ⋄ _←⍺ ⍺⍺ ⍵                        ⍝ apply function.
        data _←⎕PROFILE¨('data'name)'reset'     ⍝ profiling data for ⍺⍺.
        lines count←1↓¨1↓3↑↓⍉data               ⍝ lines visited and hit-count.
        code←⎕CR name                           ⍝ source code for ⍺⍺.
        v←{0}¨↓code                             ⍝ initial hits vector.
        v[lines+⎕IO]←count                      ⍝ insert hits.
        v,code                                  ⍝ hits followed by source.
    }

Bugs:

[1] An inner D-fn that has executable code on its first (or only) line:

        inner←{⍺←0
    or:
        free←{⍵~¨⍺+(⍴⍵)↑cvex}

    reports a count corresponding to the definition rather than the execution of
    the  first  line.  In order to examine such cases in more detail, split them
    over several lines. For example in queens below, splitting the definition of
    inner  function  "free" over two lines, shows it defined once but called 862
    times:

          1     free←{
        862           ⍵~¨⍺+(⍴⍵)↑cvex}           ⍝ Unchecked squares.

[2] A  subject  operand  function  that  happened to contain either of the names
    "_mdf_c" or "_mdf_f" would produce unpredictable results.

[3] The  subject  operand function may not be more exotic than a named simple D-
    fn. In particular, it may not be:

        a derived function as in:

            foo¨mdf 0
            foo∘goo mdf 0
            foo mdf mdf 0

        an unnamed function such as:

            {⍵ ⍵}mdf 0

Examples:

⍝ Monitored application of various functions:

      osc mdf 27
  0 osc←{               ⍝ Oscillate - probably returns 1.
112     1=⍵:1
111     2|⍵:∇ 1+3×⍵
 70         ∇ ⍵÷2
  0 }

      105 gcd mdf 330
0 gcd←{       ⍝ Greatest common divisor.
4     ⍵=0:⍺
3     ⍵ ∇ ⍵|⍺
0 }

      sieve mdf 2 to 100
0 sieve←{             ⍝ Sieve of Eratosthenes.
5     ⍺←⍬             ⍝ Default no primes yet.
5     nxt←1↑⍵         ⍝ Next prime, and
5     msk←×nxt|⍵      ⍝ ... mask of non-multiples.
5     ^/1↓msk:⍺,⍵     ⍝ All non multiples - finished.
4     (⍺,nxt)∇ msk/⍵  ⍝ Sieve remainder.
0 }

      queens mdf 8
  0 queens←{⎕ML ⎕IO←0                   ⍝ The N-queens problem.
  1
  1     search←{                        ⍝ Search for all solutions.
863         (⊂⍬)∊⍵:0⍴⊂⍬                 ⍝ stitched: abandon this branch.
583         0=⍴⍵:rmdups ⍺               ⍝ all done: solution!
537         hd tl←(⊃⍵)(1↓⍵)             ⍝ head 'n tail of remaining ranks.
537         next←⍺∘,¨hd                 ⍝ possible next steps.
537         rems←hd free¨⊂tl            ⍝ unchecked squares.
537         ↑,/next ∇¨rems              ⍝ ... in following ranks.
  0     }
  1
  1     cvex←(1+⍳⍵)×⊂¯1 0 1             ⍝ Checking vectors.
  1
  1     free←{⍵~¨⍺+(⍴⍵)↑cvex}           ⍝ Unchecked squares.
  1
  1     rmdups←{                        ⍝ Ignore duplicate solution.
 46         rots←{{⍒⍵}\4/⊂⍵}            ⍝ 4 rotations.
 46         refs←{{⍋⍵}\2/⊂⍵}            ⍝ 2 reflections.
 46         best←{(⊃⍋↑⍵)⊃⍵}             ⍝ best (=lowest) solution.
 46         all8←,↑refs¨rots ⍵          ⍝ all 8 orientations.
 46         (⍵≡best all8)⊃⍬(,⊂⍵)        ⍝ ignore if not best.
  0     }
  1
  1     fmt←{                           ⍝ Format solution.
  1         chars←'·⍟'[(↑⍵)∘.=⍳⍺]       ⍝ char array of placed queens.
  1         expd←1↓,↑⍺⍴⊂0 1             ⍝ expansion mask.
  1         ↑¨↓↓expd\chars              ⍝ vector of char matrices.
  0     }
  1
  1     squares←(⊂⍳⌈⍵÷2),1↓⍵⍴⊂⍳⍵        ⍝ initial squares
  1
  1     ⍵ fmt ⍬ search squares          ⍝ all distinct solutions.
  0 }

      3 ack mdf 3                       ⍝ Ackermann's function.
   0 ack←{
2432     ⍺=0:⍵+1
1244     ⍵=0:(⍺-1)∇ 1
1187         (⍺-1)∇ ⍺ ∇ ⍵-1
   0 }

See also: ticks cmpx time

Back to: contents

Back to: Workspaces

Trouble seeing APL font?