It seems concatenating, especially vectors, is quite common.
And it would be nice if concatenating with mixed scalar and vector 
arguments was type-stable.

I did some playing around and although I don't fully understand the impact, 
I [think I] was able to get a type-stable version of vcat with 
vector/scalar arguments.

Essentially widen signatures of vcat to allow mixed arguments and introduce 
full for Number:

abstractarray.jl line 742
vcat(V::AbstractVector...) = typed_vcat(promote_eltype(V...), V...)
vcat(V::Union{Number,AbstractVector}...) = typed_vcat(promote_eltype(V...), 
V...)

abstractarray.jl line 745
function typed_vcat(T::Type, V::AbstractVector...)
function typed_vcat(T::Type, V::Union{Number,AbstractVector}...)

Base.full(x::Number) = [x]

Here full acts as a sort of "promotion to vector".
This might seem a somewhat twisted use of full, so in typed_cat, I 
tried substituting:
a = similar(isa(V[1], AbstractVector) ? full(V[1]) : [V[1]], T, n)
instead of
a = similar(full(V[1]), T, n)
but it didn't work.


I don't really know how to try this out directly, so I overwrote methods:

function Base.vcat(V::Union{Number,AbstractVector}...)
    #println("my vcat")
    Base.typed_vcat(Base.promote_eltype(V...), V...)
end

function Base.typed_vcat(T::Type, V::Union{Number,AbstractVector}...)
    #println("my typed_vcat")
    n::Int = 0
    for Vk in V
        n += length(Vk)
    end
    a = similar(full(V[1]), T, n)
    pos = 1
    for k=1:length(V)
        Vk = V[k]
        p1 = pos+length(Vk)-1
        a[pos:p1] = Vk
        pos = p1+1
    end
    a
end

function Base.full(x::Number)
    #println("my full")
    [x]
end



On Friday, April 22, 2016 at 3:32:27 AM UTC+10, Jeremy Kozdon wrote:

> In a class I'm teaching the students are using Julia and I couldn't for 
> the life of me figure out why one of my students codes was allocating a lot 
> of memory.
>
> I finally paired it down the following example that I don't understand:
>
> function version1(N)
>   b = [1;zeros(N-1)]
>   println(typeof(b))
>   for k = 1:N
>     for j = 1:N
>       b[j] += k
>     end
>   end
> end
>
>
> function version2(N)
>   b = zeros(N)
>   b[1] = 1
>   println(typeof(b))
>   for k = 1:N
>     for j = 1:N
>       b[j] += k
>     end
>   end
> end
>
> N = 1000
> println("compiling..")
> @time version1(N)
> version2(N)
> println()
> println()
>
> println("Version 1")
> @time version1(N)
> println()
>
> println("Version 2")
> @time version2(N)
>
> The output of this (without the compiling output) in v0.4.5 is:
>
> Version 1
> Array{Float64,1}
>   0.092473 seconds (3.47 M allocations: 52.920 MB, 3.24% gc time)
>
> Version 2
> Array{Float64,1}
>   0.001195 seconds (27 allocations: 8.828 KB)
>
> Both version produce the same type for Array b, but in version1 every time 
> through the loop allocation happens and in the version2 the only allocation 
> is of the initial array.
>
> I've not run into this one before (because I would never do version1), but 
> as all of us that teach know students will always surprise you with their 
> approaches.
>
> Any help understanding what's going on would be appreciated.
>

Reply via email to