How to convert a Vector of MVectors to a Matrix

I have the following code:

using StaticArrays

function test()
    X = range(0, stop=10, length=8)
    X_vec = Vector{MVector{8, Float32}}(undef, 10)
    for i in range(1, stop=10)
        X_vec[i] = X
    end
    return X_vec
end

I get the following output:

julia> vec=test()
10-element Vector{MVector{8, Float32}}:
 [0.0, 1.4285715, 2.857143, 4.285714, 5.714286, 7.142857, 8.571428, 10.0]
 [0.0, 1.4285715, 2.857143, 4.285714, 5.714286, 7.142857, 8.571428, 10.0]
 [0.0, 1.4285715, 2.857143, 4.285714, 5.714286, 7.142857, 8.571428, 10.0]
 [0.0, 1.4285715, 2.857143, 4.285714, 5.714286, 7.142857, 8.571428, 10.0]
 [0.0, 1.4285715, 2.857143, 4.285714, 5.714286, 7.142857, 8.571428, 10.0]
 [0.0, 1.4285715, 2.857143, 4.285714, 5.714286, 7.142857, 8.571428, 10.0]
 [0.0, 1.4285715, 2.857143, 4.285714, 5.714286, 7.142857, 8.571428, 10.0]
 [0.0, 1.4285715, 2.857143, 4.285714, 5.714286, 7.142857, 8.571428, 10.0]
 [0.0, 1.4285715, 2.857143, 4.285714, 5.714286, 7.142857, 8.571428, 10.0]
 [0.0, 1.4285715, 2.857143, 4.285714, 5.714286, 7.142857, 8.571428, 10.0]

This looks like a Matrix, but it isn’t. How can I get the n-th column out of this vector of MVectors?

This does not work:

vec[:, 2]

Is reduce(hcat, v) good enough? I you don’t want to actually create the matrix but only index into vec as if it were a matrix you can use RecursiveArrayTools.jl:

using RecursiveArrayTools

m = VectorOfArray(vec)

m[:,1] # works

m[1,:] # works as well
2 Likes

I can confirm that both suggested methods work, and that using RecursiveArrayTools is much faster for large arrays.

using StaticArrays, RecursiveArrayTools

const time_steps = 20*3600

function test()
    X = range(0, stop=10, length=8)
    X_vec = Vector{MVector{8, Float32}}(undef, time_steps)
    for i in range(1, stop=time_steps)
        X_vec[i] = X .+ i
    end
    return X_vec
end

function extract1(vec)
    return reduce(hcat, vec)[end,:]
end

function extract2(vec)
    return VectorOfArray(vec)[end,:]
end

vec = test()


@time extract1(vec); 
@time extract1(vec); # 2.47 MiB per h of flight test data
@time extract2(vec);
@time extract2(vec); # 0.28 MiB per h of flight test data

Output:

julia> includet("src/Reshape.jl")
  0.116098 seconds (363.46 k allocations: 24.692 MiB, 7.72% gc time, 98.42% compilation time)
  0.002370 seconds (4 allocations: 2.472 MiB)
  0.033703 seconds (125.05 k allocations: 7.694 MiB, 99.14% compilation time)
  0.000277 seconds (3 allocations: 281.344 KiB)

Remaining question: Is it possible to obtain a view with RecursiveArrayTools and reduce the memory allocations even further?

To answer my own question: Yes, views improve the performance even further:

function extract1(vec)
    return @view reduce(hcat, vec)[end,:]
end

function extract2(vec)
    return @view VectorOfArray(vec)[end,:]
end

Output:

julia> includet("src/Reshape.jl")
  0.109534 seconds (399.17 k allocations: 26.441 MiB, 8.09% gc time, 98.41% compilation time)
  0.001820 seconds (3 allocations: 2.197 MiB)
  0.015507 seconds (91.37 k allocations: 5.756 MiB, 99.91% compilation time)
  0.000003 seconds (2 allocations: 64 bytes)