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
This is explored further in the experimental "Function Results Edition" of
Dyalog, See: http://dfns.dyalog.com/downloads/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 time profile
Back to: contents
Back to: Workspaces