Dispatch on argument values rather than (just) types?


#1

Dear All,

A rather broad question - I have cases where different algorithms are faster for different parameter values. I’d like to have a collection of different methods that are called according to some conditions. Ideally, there would be some benchmarking code run to tune the methods to decide which method to call when.

Are there good (or bad) ways to dispatch based on the argument values (rather than just for types)? I know about https://github.com/toivoh/PatternDispatch.jl , but it doesn’t seem to be maintained. Could I use something like https://github.com/mauro3/SimpleTraits.jl for this?

Best wishes
Simon


#2

I’m doing that all of the time.
A simple way is to create an abstract type and a set of singleton types,
and then have a function that, given a value, returns an instance of one of those types.
You then write your top level function to call that, and then write a set of methods that get passed the result of that (to dispatch on), as well as the parameter value itself.


#3
julia> _f(::Val{1}) = -2
_f (generic function with 1 method)

julia> _f(::Val{2}) = -4
_f (generic function with 2 methods)

julia> _f(::Val{N}) where {N} = 2*N
_f (generic function with 3 methods)

julia> f(n) = _f(Val{n}())
f (generic function with 1 method)

julia> f(1)
-2

julia> f(2)
-4

julia> f(3)
6

#4

In general, type computations in Julia are made for things that should be done at compilation time. If you cannot do the calculation at compilation time, then you will get performance hits by using the dispatch system (but if you can do things at compilation time, then you will get performance gains!). So it’s a tool to be used wisely.

In this case, it sounds like you want to dispatch on runtime values. This means you are in the case where you will get performance losses. From what I measured this means using dispatch to do this kind of things gives about a 100ns hit per function call that is uninferred because of it. So a function barrier approach is fine if it’s not in an inner loop, but you may want to think about just writing out a conditional instead. Due to branch prediction, solving an equation repeatedly with a given set of parameter values is something that will make the branch cost almost disappear on modern CPUs, so it doesn’t really need to be avoided. Though this may be not as nice looking stylistically as using dispatch would.

Ultimately, it’s a choice of style and performance, but I think it’s wise, not just for performance but also for style, to keep type computations and dispatch for compile-time computations.


#5

Base.Cartesian.@nif may help, if there is a pattern or a lot of comparisons to be made that would make actually writing out the control flow statements too verbose / tedious.


#6

This might be a heck of a lot easier on Julia 0.7 with constant propagation. Of course, this depends whether the values you’re dispatching are indeed constants…