Adding the type parameter does indeed do the trick. I wasn't aware that 
there were cases where the compiler wouldn't specialise, but it makes sense.

(I used Val{T} instead of Val{T}() because the docs at 
http://docs.julialang.org/en/release-0.4/manual/types/ say "For consistency 
across Julia, the call site should always pass a Val type rather than 
creating an instance.")

Replacing the Val with a tuple works in this case because all the types in 
the example are Int64, but if I try eg 

@code_warntype reshape(
  Vector{Tuple{Int64, Float64, Int64}}(0), 
  Vector{Tuple{Int64, Float64}}(0), 
  (1, 2), 
  )

then I can see it boxing again, because without knowing the actual index 
numbers at compile time it can't prove that the types line up. That's fine 
though, I'm happy using Val.

I noticed a similar problem though with sort:

f() = begin
  xs = [(a,b,c) for (a,b,c) in zip(ids(), ids(), ids())]
  @time sort!(xs, alg=QuickSort)   0.215447 seconds (1 allocation: 80 bytes)
end

f() = begin
  xs = [(a,b,Float64(c)) for (a,b,c) in zip(ids(), ids(), ids())]
  @time sort!(xs, alg=QuickSort) #   2.563458 seconds (91.95 M allocations: 
2.055 GB, 13.40% gc time)
end

If I make my own immutable type instead of using tuples then it works fine 
in both cases. I'm seeing similar behaviour in some of my own code too.

dedup_sorted{X}(xs::Vector{X}) = begin
  ys = Vector{X}(0)
  last = xs[1]
  push!(ys, last)
  for x in xs
    if x != last
      push!(ys, x)
      last = x
    end
  end
  xs
end

f() = begin
  xs = [(a,b,c) for (a,b,c) in zip(ids(), ids(), ids())]
  sort!(xs, alg=QuickSort)
  @time dedup_sorted(xs) # 0.020497 seconds (20 allocations: 25.500 MB)
end

f() = begin
  xs = [(a,b,Float64(c)) for (a,b,c) in zip(ids(), ids(), ids())]
  sort!(xs, alg=QuickSort)
  @time dedup_sorted(xs) # 0.137487 seconds (5.47 M allocations: 138.703 
MB, 16.06% gc time)
end

Again, it works fine if I make my own immutable type types. Is this the 
same kind of heuristic at work? Is it reluctant to specialise on tuples 
with complex types?

On Tuesday, 29 December 2015 10:12:21 UTC, Mike Innes wrote:
>
> I think this is down to Julia avoiding specialisation on the `ykey` type 
> in `reshape`, which sometime happens if it's (heuristically) determined 
> that doing so will prevent large amounts of unnecessary code being 
> generated. You can always force specialisation with a type parameter:
>
> reshape{T}(xs, ys, ykey::Type{Val{T}}) = begin
>
> However, this is unnecessary if you make the types simpler. Changing 
> `::Type{Val{T}}` to `::Val{T}` and `Val{T}` to `Val{(1,3)}()` will do the 
> trick if you want to stick with that approach, but just use a tuple 
> directly:
>
> @generated construct(key::Tuple, value) = begin
>   :(begin
>       $(Expr(:meta, :inline))
>       tuple($([:(value[key[$i]]) for i in 1:length(key.parameters)]...))
>     end)
> end
>
> reshape(xs, ys, ykey) = begin
>   for x in xs
>     push!(ys, construct(ykey, x))
>   end
>   return ys
> end
>
> reshape([(10,11,12),(13,14,15)],NTuple{2,Int}[],(1,3))
>
> Above: 0.021351 seconds (500.00 k allocations: 30.518 MB, 17.76% gc time)
> Type{Val{T}} reshape2: 0.021599 seconds (500.00 k allocations: 30.518 MB, 
> 19.60% gc time)
>
>
> On Tue, 29 Dec 2015 at 07:16 Jamie Brandon <[email protected] 
> <javascript:>> wrote:
>
>> I have two versions of this reshape function - 
>> https://gist.github.com/jamii/62b3c3695fba95f3f09b
>>
>> The produce near-identical ast from code_warntype - 
>> https://www.diffchecker.com/awnv9zvv
>>
>> But code_lowered shows that reshape naively boxes tuples whereas reshape2 
>> does something much more complicated - 
>> https://www.diffchecker.com/jb6aurpl
>>
>> The result is that reshape2 is much faster:
>>
>> reshape: 0.316513 seconds (2.00 M allocations: 95.036 MB, 5.41% gc time)
>> reshape2:  0.218963 seconds (41 allocations: 34.001 MB, 0.85% gc time)
>>
>> What's going on? How can I make reshape avoid boxing?
>>
>

Reply via email to