I am trying to benchmark a code that calls append, but the results do not seem to make sense. The minimal working example which I do not understand is:
julia> using BenchmarkTools
julia> x = [0]; y = zeros(100); @btime append!($x,$y)
376.261 ns (0 allocations: 0 bytes)
235230201-element Array{Int64,1}:
0
0
...
As you can see, @btime returns an enormous vector.
This is part of me trying to benchmark append! vs. push! in a simple example:
using BenchmarkTools
using Test
# using push!
function f!(x)
for i in 1:100
push!(x,i)
end
end
# using append!
function g!(x)
xlength = length(x)
append!(x,Vector{Int64}(undef,100))
for i in 2:101
x[i] = i-1
end
end
# creating a new vector
function h(x)
y = Vector{Int64}(undef,length(x)+100)
for i in 1:length(x)
y[i] = x[i]
end
for i in 2:101
y[i] = i-1
end
return y
end
xf = [0] ; f!(xf)
xg = [0] ; g!(xg)
xh = [0] ; y = h(xh)
@test xf == xg == y
print("push! :"); xf = [0]; @btime f!($xf)
print("append!:"); xg = [0]; @btime g!($xg)
print("new vec:"); xh = [0]; @btime h($xh)
(I do not understand either how can push not allocate any memory)
My expectation was that append! would be the fastest alternative here, but it seems that the benchmarking does not make sense, considering the enormous ammount of allocations it is implying.
That resizing is fastest and push the slowest is expected. What I did not expect is that using append is so much slower than even creating a new vector. I would expect it to be almost identical to the resizing option, as I am appending a undef array.
Code:
using BenchmarkTools
using Test
const N = 100
# using push!
function f!(x)
for i in 1:N
push!(x,i)
end
end
# using append!
function g!(x)
xlength = length(x)
append!(x,Vector{Int64}(undef,N))
for i in 2:N+1
x[i] = i-1
end
end
# creating a new vector
function h(x)
y = Vector{Int64}(undef,length(x)+N)
for i in 1:length(x)
y[i] = x[i]
end
for i in 2:N+1
y[i] = i-1
end
return y
end
# Resizing
function m!(x)
resize!(x,length(x)+N)
for i in 2:N+1
x[i] = i-1
end
end
xf = [0] ; f!(xf)
xg = [0] ; g!(xg)
xh = [0] ; y = h(xh)
xm = [0] ; m!(xm)
@test xf == xg == y == xm
print("push! :"); xf = [0]; @btime f!(copy($xf))
print("append!:"); xg = [0]; @btime g!(copy($xg))
print("new vec:"); xh = [0]; @btime h(copy($xh))
print("resize :"); xm = [0]; @btime m!(copy($xm))
The difference here is pretty small, but I don’t think it’s fair to say that append is slower than creating a new vector because your “append” benchmark also creates a new vector. You are doing:
append!(x,Vector{Int64}(undef,100))
which first creates a new vector of length 100 and then appends that to x. That means you’re doing all of the work to create a new vector and all the work to resize x to hold that new vector. It’s not surprising that this is slower than only creating a new vector and not appending it to x.
Your results should be different if you were to instead append an existing vector to x.