Issue 1:
I'm trying to use sizehint! + push! to fill an array, but it is much slower than the equivalent operation using resize! and indexing. Benchmarks below.

I'm not surprised there's some overhead, and I'd be willing to accept some just to avoid potential bugs resulting from uninitialized data, but the overhead is 10x:

julia> VERSION
v"1.3.0-DEV.377"

julia> f!(arr, N) = ( sizehint!(arr, N); for i in 1:N; push!(arr, i); end; arr )
f! (generic function with 1 method)

julia> g!(arr, N) = ( resize!(arr, N); for i in 1:N; arr[i] = i; end; arr )
g! (generic function with 1 method)

julia> f!(Int[], 1_000_000) == g!(Int[], 1_000_000)
true

julia> @benchmark f!(arr, 1_000_000) setup=(arr=Int[])
BenchmarkTools.Trial:
memory estimate: 7.63 MiB
allocs estimate: 1
--------------
minimum time: 3.717 ms (0.00% GC)
median time: 3.811 ms (0.00% GC)
mean time: 3.870 ms (0.00% GC)
maximum time: 6.022 ms (0.00% GC)
--------------
samples: 1271
evals/sample: 1

julia> @benchmark g!(arr, 1_000_000) setup=(arr=Int[])
BenchmarkTools.Trial:
memory estimate: 7.63 MiB
allocs estimate: 1
--------------
minimum time: 392.328 μs (0.00% GC)
median time: 419.459 μs (0.00% GC)
mean time: 433.387 μs (0.00% GC)
maximum time: 2.653 ms (0.00% GC)
--------------
samples: 10000
evals/sample: 1

Almost all time is spent in the function _growend!, which in turn is backed by a C function so I can't easily pin-point on what line the slowness is. I have reproduced this in v1.0.3 as well as latest master.

Issue 2:
When looking over my code_native, I recently saw that every push! costs me a call into the runtime library. This appears...excessive, at least for the standard case where the push can be accommodated without copying.

So, lets compare the push! and the fastpush!

First, lets look at what a vector actually is:

struct array_T
data :: Ptr{Void}
length:: UInt64
flags::UInt16
elsize::UInt16
offset::UInt32
nrows::UInt64
ncols_maxsz::UInt64
stuff1::UInt64
stuff2::UInt64
stuff3::UInt64
end

This gives us the fastpush:

@inline function fastpush!(A::Vector{T},val::T) where T
if isbits(T)
arr = unsafe_load(convert(Ptr{array_T}, pointer_from_objref(A)))
if arr.length + arr.offset < arr.ncols_maxsz
unsafe_store!(convert(Ptr{UInt64}, pointer_from_objref(A)), arr.length+1, 2)
unsafe_store!(convert(Ptr{UInt64}, pointer_from_objref(A)), arr.length+1, 4)
@inbounds A[arr.length+1] = val
else
push!(A,val)
end
else
push!(A,val)
end
A
end

And lets run a race:

function pushtest(A,n)
resize!(A, 0);
for i = 1:n push!(A,i) end
nothing
end

function fastpushtest(A,n)
resize!(A, 0)
for i = 1:n fastpush!(A,i) end
nothing
end

function settest(A,n)
resize!(A, n)
for i = 1:n A[i]=i end
nothing
end

yielding:

Nm = 100_000_000; A = Vector{Int}(Nm); resize!(A, 0);
#after warmup:

@time settest(A,Nm); @time settest(A,Nm);
0.356040 seconds (4 allocations: 160 bytes)
0.326640 seconds (4 allocations: 160 bytes)

@time pushtest(A,Nm); @time pushtest(A,Nm);
0.899436 seconds (4 allocations: 160 bytes)
0.895555 seconds (4 allocations: 160 bytes)

@time fastpushtest(A,Nm); @time fastpushtest(A,Nm);
0.577715 seconds (4 allocations: 160 bytes)
0.606671 seconds (4 allocations: 160 bytes)

Hence, a 30% speedup is possible if we manage to get the fastpath inlined.

It would mayhaps be nice if it was somehow possible to make llvm aware of julia's runtime lib. For example, compile to llvm_IR (using clang or dragonegg?), and call into a nice library of llvm code including optimization-relevant metadata, and possibly inline current ccall-functions.

Not sure whether this performance problem is worth trying to fix (and how much more expensive it is if we lose important registers on the call). It is easy to avoid, though, by implementing the checks by hand (for tight loops that need to push a lot). In a perfect world, the optimizer would reduce to only a single check for unrolled pushing loops. In my applications, I do enough other things between push!/pop! that this ends up being benign.

PS. This is related to #24901, which removes the boundcheck from push!/pop!.

I also posted this in discourse.julialang.org before. The fastpush is deliberately not a pull request, because it is dirty and architecture dependent.