Hi,
I’ve got a problem that can be nicely solved with closures. I’ve explored the alternative, which is to make a custom callable object. I would like to dispatch them both as a function, so I’ve also tried making that callable object a subtype of Function
.
Both the closure and function subtype have a 3x slower runtime than the callable object when using a broadcast call. This is a rather massive performance hit for a determining step.
Traceur.@trace
doesn’t find anything to bark at. When using broadcast-calls, the output of @code_warntype
is beyond my ability to parse. All cases have the odd T::Any
which doesn’t seem to be used anywhere.
Here is are examples (not exactly minimal, sadly). The object precomputes the result and holds it in a lookup table. Values beyond the table’s range are extrapolated. Here are the three implementations. @benchmark
results follow, then the boilerplate benchmarking code (each case is run in a function to avoid global scope issues).
# closure
function closure(dt::Real, grid::Array{<:Number,1})
I1 = cumsum_kbn(grid)
Im = I1[end]
lut = cumsum_kbn(I1)
t-> begin
i = Int(t/dt)+1
i <= length(lut) ? lut[i] : t*Im
end
end
# Callable object
immutable Gridded{Tg<:Real, Tcf<:Number}
dt::Tg
Im::Tcf
lut::Array{Tcf,1}
end
function Gridded(dt, grid) # constructor
I1 = cumsum_kbn(grid)
Gridded(dt, I1[end], cumsum_kbn(I1))
end
function (cf::Gridded)(t) # callable
i = Int(t/cf.dt)+1
i <= length(cf.lut) ? cf.lut[i] : t*cf.Im
end
# Subtype of Function
immutable GriddedFunc{Tg<:Real, Tcf<:Number} <: Function
dt::Tg
Im::Tcf
lut::Array{Tcf,1}
end
function GriddedFunc(dt, grid) # constructor
I1 = cumsum_kbn(grid)
GriddedFunc(dt, I1[end], cumsum_kbn(I1))
end
function (cf::GriddedFunc)(t) # callable
i = Int(t/cf.dt)+1
i <= length(cf.lut) ? cf.lut[i] : t*cf.Im
end
Benchmark results:
- closure
BenchmarkTools.Trial:
memory estimate: 4.47 KiB
allocs estimate: 28
--------------
minimum time: 12.763 μs (0.00% GC)
median time: 13.858 μs (0.00% GC)
mean time: 18.482 μs (4.79% GC)
maximum time: 4.703 ms (98.44% GC)
--------------
samples: 10000
evals/sample: 1
- callable object
BenchmarkTools.Trial:
memory estimate: 3.38 KiB
allocs estimate: 2
--------------
minimum time: 4.480 μs (0.00% GC)
median time: 4.793 μs (0.00% GC)
mean time: 7.001 μs (5.59% GC)
maximum time: 702.671 μs (98.24% GC)
--------------
samples: 10000
evals/sample: 7
- callable <: Function
BenchmarkTools.Trial:
memory estimate: 4.47 KiB
allocs estimate: 28
--------------
minimum time: 12.763 μs (0.00% GC)
median time: 13.493 μs (0.00% GC)
mean time: 17.480 μs (4.67% GC)
maximum time: 4.692 ms (97.87% GC)
--------------
samples: 10000
evals/sample: 1
Benchmarking code
function bench_closure()
a = zeros(Float64, 1000)
a[1] = 1.0
dt = 1.0
f_closure = closure(dt, a)
ts = linspace(0, 2000, 2001)
@benchmark $f_closure.($ts)
end
function bench_callable()
a = zeros(Float64, 1000)
a[1] = 1.0
dt = 1.0
gridded = Gridded(dt, a)
ts = linspace(0, 2000, 2001)
@benchmark $gridded.($ts)
end
function bench_callable_function()
a = zeros(Float64, 1000)
a[1] = 1.0
dt = 1.0
gridfun = GriddedFunc(dt, a)
ts = linspace(0, 2000, 2001)
@benchmark $gridfun.($ts)
end