rslt ← {larg} (land ##.cf rand) rarg        ⍝ Ratio of operand timings.

(cf. compare, abbrv. confer(arch.) [Lat: conferre:com-:together + ferre:bring])

Phil  Last supplies this operator, which reports the ratio of the times taken to
apply its left and right operand functions to (or between) its argument(s).

    ⍺ f ∇∇ g ⍵  ←→  (⍺ f time ⍵) ÷ ⍺ g time ⍵
      f ∇∇ g ⍵  ←→  (  f time ⍵) ÷   g time ⍵

Technical notes:

Phil points out that we could do some interesting code transformations:

    cf←{⍺←{⍵}           ⍝ Compare operand timings.
        a←⍺ ⋄ f←⍺⍺ ⋄ g←⍵⍵ ⋄ w←⍵ ⋄ t←{⍬⍴2↓⎕AI-(⍺⍺/⍳1+⍵){⍵}⎕AI}
        ÷/1{⊃∇/⍺{(1000<+/⍵)↓(2×⍺)⍵}({a f w}t ⍺)({a g w}t ⍺)}1
    }

Spreading the code over a number of lines, we see:

    cf←{⍺←{⍵}                           ⍝ Compare operand timings.
        a←⍺                             ⍝ naming of ⍺
        f←⍺⍺                            ⍝           ⍺⍺
        g←⍵⍵                            ⍝           ⍵⍵
        w←⍵                             ⍝            ⍵
        t←{                             ⍝ ⍺⍺-timing operator
            ⍬⍴2↓⎕AI-(⍺⍺/⍳1+⍵){⍵}⎕AI     ⍝ time ⍺⍺ ⍺⍺ .. ⍺⍺ applications.   [1]
        }                               ⍝      └────⍵────┘
        ÷/1{                            ⍝ ratio of (initially 1) applications.
            ⊃∇/⍺{                       ⍝ repetition while 2-item vector from:
                (1000<+/⍵)↓(2×⍺)⍵       ⍝ doubling apps while time < 1 sec.
            }({a f w}t ⍺)({a g w}t ⍺)   ⍝ timing each function f and g.    [2]
        }1
    }

cf keeps doubling the number of function applications in line[1] until the total
time spent is at least one second.

Notice  that the operands of the timing operator t: {a f w} & {a g w} in line[2]
are constant functions in that they ignore any arguments. Phil notes that we can
derive such constant function without having to name their arguments. So instead
of:

    a←⍺   ┐
    f←⍺⍺  ├─── {a f w}
    g←⍵⍵  ├─── {a g w}
    w←⍵   ┘

we can say:
                     ┐
    ff←{⍵}∘(⍺∘⍺⍺)∘⍵  ├─── ff
              ¯¯     │
    gg←{⍵}∘(⍺∘⍵⍵)∘⍵  ├─── gg
              ¯¯     ┘

You can try this in the session with a constant-function-making operator:

    mkff←{ ff∘←{⍵}∘(⍺∘⍺⍺)∘⍵ }   ⍝ make constant function:   ff←{⍺ ⍺⍺ ⍵}.

      2 + mkff 3                ⍝ ff←{2+3}

      ff 99
5

Although not necessary in this case, we could extend mkff to generate a function
that ignores both its right and _left_ arguments by binding a further {⍵}∘:

    mkff←{ ff∘←{⍵}∘({⍵}∘(⍺∘⍺⍺)∘⍵) }     ⍝ make constant function ff←{⍺ ⍺⍺ ⍵}.
               ¯¯¯¯¯            ¯
    20 + mkff 30                        ⍝ ff←{20+30}

    ff 4                                ⍝ right and
50
    2 ff 3                              ⍝ left arg ignored.
50

(muse:
    If  Dyalog were extended to allow functions (and operators) to return funct-
    ions as results, the above might look a little neater:

        mk∆ ← {⍺←{⍵} ⋄ {⍵}∘({⍵}∘(⍺∘⍺⍺)∘⍵) }     ⍝ constant function: {⍺ ⍺⍺ ⍵}.

        gg ← 3 × mk∆ 4                          ⍝ gg←{3×4}

        'do' gg 'zen'
    12

    See: http://www.dyalog.com/dfnsdws/fre.pdf
)

Incorporating this technique into our cf operator yields:

    cf←{⍺←{⍵}                           ⍝ Compare operand timings.
        ff←{⍵}∘(⍺∘⍺⍺)∘⍵                 ⍝ naming of {⍺ ⍺⍺ ⍵}
        gg←{⍵}∘(⍺∘⍵⍵)∘⍵                 ⍝           {⍺ ⍵⍵ ⍵}
        t←{                             ⍝ ⍺⍺-timing operator
            ⍬⍴2↓⎕AI-({⍵}∘⍺⍺/⍳1+⍵){⍵}⎕AI ⍝ time ⍺⍺ ⍺⍺ .. ⍺⍺ applications.   [1]
        }                               ⍝      └────⍵────┘
        ÷/1{                            ⍝ ratio of (initially 1) applications.
            ⊃∇/⍺{                       ⍝ repetition while 2-item vector from:
                (1000<+/⍵)↓(2×⍺)⍵       ⍝ doubling apps while time < 1 sec.
            }(ff t ⍺)(gg t ⍺)           ⍝ timing each function ff and gg.  [2]
        }1  ⍝ ¯¯      ¯¯
    }

Next, we can avoid naming ff and gg by binding them with the timing operator and
passing the resulting derived functions as operands to line[2]:

    cf←{⍺←{⍵}                           ⍝ Compare operand timings.
        t←{                             ⍝ ⍺⍺-timing operator
            ⍬⍴2↓⎕AI-({⍵}∘⍺⍺/⍳1+⍵){⍵}⎕AI ⍝ time ⍺⍺ ⍺⍺ .. ⍺⍺ applications.   [1]
        }                               ⍝      └────⍵────┘
        ÷/1 {⍵}∘(⍺∘⍺⍺)∘⍵ t{             ⍝ ratio of (initially 1) applications.
            ⊃∇/⍺{                       ⍝ repetition while 2-item vector from:
                (1000<+/⍵)↓(2×⍺)⍵       ⍝ doubling apps while time < 1 sec.
            }(⍺⍺ ⍺)(⍵⍵ ⍺)               ⍝ timing each function ⍺⍺ and ⍵⍵.  [2]
        }({⍵}∘(⍺∘⍵⍵)∘⍵ t)1              ⍝ ⍵⍵ timing function.
    }

Unfortunately,  in the absence of a higher level "hyperator" that takes operator
t  as operand, this is as far as we can go, unless we are prepared to repeat the
definition  of  t  for left and right operands. Either way, we wind up with a cf
operator that has no local assignment and no guards; it is a simple expression:

    cf←{⍺←{⍵}           ⍝ Compare operand timings.
        ÷/1{⍵}∘({⍵}∘(⍺∘⍺⍺)∘⍵){⍬⍴2↓⎕AI-(⍺⍺/⍳1+⍵){⍵}⎕AI}{
            ⊃∇/⍺{
                (1000<+/⍵)↓(2×⍺)⍵
            }(⍺⍺ ⍺)(⍵⍵ ⍺)
        }({⍵}∘({⍵}∘(⍺∘⍵⍵)∘⍵){⍬⍴2↓⎕AI-(⍺⍺/⍳1+⍵){⍵}⎕AI})1
    }

Examples:

    {(+/⍵)÷⍴⍵} cf {+/⍵÷⍴⍵}  ⍳1000       ⍝ compare codings of mean.
0.164

    ⎕av (,⍨¨) cf (,¨⍨) ⎕av              ⍝ commute-each vs. each-commute.
1.35

See also: cmpx

Back to: contents

Back to: Dyalog APL

Trouble seeing APL font?