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.

Reply via email to