On Thu, Jul 2, 2015 at 10:14 AM, Dan Schmidt <[email protected]> wrote:
> On Thursday, July 2, 2015 at 9:51:02 AM UTC-4, Yichao Yu wrote:
>>
>> Not sure if you could easily figure this one out but the issue here is
>> that julia only specialize on the `Type{}` of a datatype if you
>> specify so.
>>
>> i.e. `f(x) = ...` called with `f(Int)` will only specialize on
>> `f(::DataType)`. If you want to specialize on `f(::Type{Int})`, you
>> need to either define `f(::Type{Int})` or more generalized
>> `f{T}(::Type{T})`
>>
>> In your example. your second `map_f` is only specialized on
>> `map_f(::Vector{Float64}, ::DataType)` when called with the type (in a
>> sense not so much better than the first version). Therefore, the call
>> to `call_f` need to go through runtime dispatch which involve a whole
>> bunch of random stuff including boxing (which allocates). You can fix
>> this by defining `map_f{T}(v::Vector{Float64}, f_type::Type{T})`
>> instead.
>>
>> If you are on 0.4, the `call_f` can be replaced with a `call` overload
>> and the second version should be the perferred way to do this and in
>> the long term this is very likely how anonymous functions (or all
>> functions) works.
>
>
> Thanks, that makes perfect sense. Changing the signature of map_f as you
> suggest did indeed succeed in recovering the lost performance.
>
> What I still don't understand is why @codewarn_type generates exactly the
> same code for all three examples (the two I was comparing above, plus the
True, and that's why I said it would be hard to figure this out by
yourself.... You can probably report this as a bug. (to make
code_typed return what julia will specialize on, possibly behind an
option)
> new one with the new signature for map_f). All copies of the code appeared
> to have call_f inlined. I'll paste the results here:
>
> Variables:
> v::Array{Float64,1}
> f_type::MulTwo
> ret::Array{Float64,1}
> #s52::Int64
> i::Int64
>
> Body:
> begin # In[2], line 2:
> ret = copy(v::Array{Float64,1})::Array{Float64,1} # line 3:
> GenSym(3) = (top(arraylen))(ret::Array{Float64,1})::Int64
> GenSym(0) = $(Expr(:new, UnitRange{Int64}, 1,
> :(((top(getfield))(Intrinsics,:select_value))((top(sle_int))(1,GenSym(3))::Bool,GenSym(3),(top(box))(Int64,(top(sub_int))(1,1)))::Int64)))
> #s52 = (top(getfield))(GenSym(0),:start)::Int64
> unless (top(box))(Bool,(top(not_int))(#s52::Int64 ===
> (top(box))(Int64,(top(add_int))((top(getfield))(GenSym(0),:stop)::Int64,1))::Bool))
> goto 1
> 2:
> GenSym(4) = #s52::Int64
> GenSym(5) = (top(box))(Int64,(top(add_int))(#s52::Int64,1))
> i = GenSym(4)
> #s52 = GenSym(5) # line 4:
> GenSym(2) =
> (top(box))(Float64,(top(mul_float))((top(arrayref))(ret::Array{Float64,1},i::Int64)::Float64,2.0))
>
> (top(arrayset))(ret::Array{Float64,1},GenSym(2),i::Int64)::Array{Float64,1}
> 3:
> unless
> (top(box))(Bool,(top(not_int))((top(box))(Bool,(top(not_int))(#s52::Int64
> ===
> (top(box))(Int64,(top(add_int))((top(getfield))(GenSym(0),:stop)::Int64,1))::Bool))))
> goto 2
> 1:
> 0: # line 6:
> return ret::Array{Float64,1}
> end::Array{Float64,1}
>
> The only difference in the version that allocates a ton (I did a real diff,
> not just an eyeball check) is that f_type::MulTwo became
> f_type::Type{MulTwo}. There are some worrisome "box"es in there but they're
> in both versions.