vcat(a'...) is quite inefficient for what it’s worth. The problem is that the code generated depends on the length of a', but that information isn’t known until runtime (rather than compiletime).
Normally, I’d recommend a reduce, but unfortunately we currently don’t have an optimized code-path for reduce(vcat, a') (unlike redunce(hcat, a)'):
julia> let a = [rand(10) for _ ∈ 1:100]
o1 = @btime vcat($a'...)
o2 = @btime reduce(vcat, $a')
o3 = @btime reduce(hcat, $a)'
o1 == o2 == o3
end
14.990 μs (509 allocations: 23.62 KiB)
78.049 μs (393 allocations: 416.27 KiB)
1.131 μs (1 allocation: 7.94 KiB)
true
Hence, if the OP doesn’t want an adjoint or permuted matrix as an output and care about performance, you can get a big benefit by rolling up your sleeves and writing your own function:
julia> function vec_o_vec_to_mat(a::Vector{Vector{T}}) where {T}
n, m = length(a), length(a[1])
@assert all(ai -> length(ai) == m, a)
A = Array{T}(undef, n, m)
@inbounds for i ∈ 1:n
A[i, :] = a[i]
end
A
end;
julia> let a = [rand(10) for _ ∈ 1:100]
o1 = @btime vcat($a'...)
o2 = @btime reduce(vcat, $a')
o3 = @btime reduce(hcat, $a)'
o4 = @btime vec_o_vec_to_mat($a)
o1 == o2 == o3 == o4
end
15.589 μs (509 allocations: 23.62 KiB)
74.820 μs (393 allocations: 416.27 KiB)
1.131 μs (1 allocation: 7.94 KiB)
1.941 μs (1 allocation: 7.94 KiB)
true
As you can see, this is almost as fast as reduce(hcat, a)', but produces a Matrix, rather than an Adjoint{<:Matrix}. The reason it’s slower is that since julia is column-major ordered, the memory layout of your vector or vectors is inefficient for building up a matrix (this is why it’s probaby a good idea to use ' or permutedims if you can).