4

According to the "performace tips" section of the Julia manual it is not advisable to go crazy with multiple dispatch. I've run into a situation where it seems I need 3 parameters for a type I'm defining. This is related to my question about using only 1 parameter for 2 possible types. I realize I could solve the difficulty presented there by simply using another parameter but then my type looks like

type mytype{R<:Real, S<:Any, T<:Any}
   x::Matrix{R}
   y::Dict{Int64, Vector{S}}
   z::Dict{T, Vector{Int64}}
end

Is this inadvisable interms of performance for having several parameters to dispatch on. Functions on mytype would then dispatch on the 3 parameters and the function arguments is that correct?

Community
  • 1
  • 1
mv3
  • 469
  • 5
  • 16

2 Answers2

6

It's fine. Do as much multiple dispatch on Types, as you want. That is what it is for.

What you don't really want to do; and what that part of the docs is getting at, is too much dispatch on Values. Which you can do, by using a Value, as a type parameter.

Doing dispatch on values tends to have the problem that it (often) results in dynamic dispatch. Which means a function calling a function that has a value as a type parameter, can not specialize, by knowing which function it will call. This is closely related to type instability. It can kill a lot of the optimiser, and make julia run slow like python.

Here is an example of some of my code that arguably goes "too far" with dispatching on value. It makes extensive use of the Val{T} type, which exists only for allowing dispatch on value. It is very expressive, and very succinct, but it is not going to run as fast as the same code, using conditionals, or dictionary lookups instead. (And that is a trade-off I am willing to make, in this case)

The docs are also getting at that you shouldn't be storing values as type parameters on your custom types. Particularly if you are not even going to dispatch on them. That is what fields are for.

Frames Catherine White
  • 27,368
  • 21
  • 87
  • 137
  • Ok thanks, I'll have to read up more on the specifics of this (types vs values). Did you notice I used 2 questions this time ;) – mv3 Oct 28 '16 at 03:40
5

That's fine. Note you can just write this as:

type mytype{R<:Real, S, T}
   x::Matrix{R}
   y::Dict{Int64, Vector{S}}
   z::Dict{T, Vector{Int64}}
end

In most cases where you're doing a non-trivial amount of calculations, strict typing (on types) will be good for performance. If the small functions you're calling inline (in many cases it will do so automatically in v0.5, but you can help it with @inline), then there's no cost to function calls anyways and you're worrying about nothing.

As always, benchmark it and see for yourself. Mostly the problem here comes from a large number of value types.

Frames Catherine White
  • 27,368
  • 21
  • 87
  • 137
Chris Rackauckas
  • 18,645
  • 3
  • 50
  • 81
  • 2
    Over-specifying, or "strict typing" arguments does not generally improve performance: http://docs.julialang.org/en/latest/manual/performance-tips/#type-declarations and is often discouraged because it may make code less generic. Use type declarations for behavioral dispatch and structure, not performance. – Isaiah Norton Oct 28 '16 at 03:40
  • 2
    On functions,yes, type information doesn't help dispatch at all. Types are different: make all of those `Any` and type-inference will not work well. Make functions loosely typed and types strict. – Chris Rackauckas Oct 28 '16 at 03:43
  • @Isaiah Wait, I'm curious about this, are you saying for example `function(n::Int64)` is no better than `function(n)` in terms of performance? – mv3 Oct 28 '16 at 03:46
  • 3
    Yes. The function will specialize on the types that are given to it on its own, so `function(n::Int64)` will restrict what can use the function, but `function(n)` will dispatch and compile a specific version for `Int64` inputs so performance-wise it's the same. – Chris Rackauckas Oct 28 '16 at 03:48
  • 1
    @ChrisRackauckas this is really good to know and not something I really understood before. I'm new to Julia (coming from matlab, python, and a tiny bit of c++) and I really like it so far, thanks for all the help – mv3 Oct 28 '16 at 03:51
  • 1
    Note though, that ``function foo{R<:Real}(n::Vector{R})`` is much better performance than `function foo(n::Vector{Real})`. (But `function bar(n::Real)` is the same as `function bar{R<:Real}(n::R)`). This is kinda what @ChrisRackauckas is getting at with be specific for types. – Frames Catherine White Oct 28 '16 at 04:11
  • @Oxinabox glad you commented that or I wouldn't have picked up on the difference. Why is `foo(n::Vector{Real})` worse, in both cases the fact that `n` must be a vector of Reals is given 'upfront'. I don't really understand this as well as I would like to. – mv3 Oct 28 '16 at 04:48
  • `Vector{Real}` could contain a mixture of `Int32`, `Float64`, `Complex128` etc. Which means it will be an array of pointers. Which means that every element will need to be de-referenced. It also means that your function is probably going to be type-unstable. – Frames Catherine White Oct 28 '16 at 05:33
  • I see so in `foo{R<:Real}(n::Vector{R})` the value `R` is one fixed concrete value so the vector is of a specific type. Where as `foo(n::Vector{Real})` the vector could be any vector of mixed values as long as each value is a subtype of real. Ok, this is starting to make sense, please correct me if I'm wrong. – mv3 Oct 28 '16 at 05:43
  • Yes, you almost never want to "use" an abstract type, and `Vector{Real}` is a vector of abstract types, so everything will have to be boxed as @Oxinabox said. I think the dispatch case here is a little bit more confusing because it's not true that `Vector{Int64}<:Vector{Real}`, so I don't even think `foo(n::Vector{Real})` would dispatch for `typeof(n)==Vector{Int64}`. I'd have to try this. To discuss this more, I recommend you go to the chatroom: https://gitter.im/JuliaLang/julia – Chris Rackauckas Oct 28 '16 at 05:50