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
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.