Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Slicing an array produces Vector{<:SubArray}, hence allocates #55

Open
prittjam opened this issue Oct 4, 2022 · 4 comments
Open

Slicing an array produces Vector{<:SubArray}, hence allocates #55

prittjam opened this issue Oct 4, 2022 · 4 comments

Comments

@prittjam
Copy link

prittjam commented Oct 4, 2022

using TensorCast, ArraysOfArrays

A = rand(3,3,10000)

@btime out = nestedview($A, 2)
  1.587 ns (0 allocations: 0 bytes)

@btime @cast b[i][j,k] := $A[i,j,k]
  99.926 ns (6 allocations: 304 bytes)
@mcabbott
Copy link
Owner

mcabbott commented Oct 4, 2022

It's in fact much larger, as you want @cast res[k][i,j] := A[i,j,k] to match the (opaque?) meaning of that 2.

Although I'm not sure this difference matters much. Anything which makes or consumers this many slices is likely to be much more expensive. This assumption is why this package makes a simple Vector{SubArray{... rather than its own array type. Once JuliaLang/Compat.jl#663 lands, it can switch to using Base's Slices type.

Do you have a real use in which this cost isn't negligible?

Note also that type-stability tends to matter for anything downstream of call:

julia> @code_warntype nestedview(A, 2)
...
Static Parameters
  T = Float64
  L = 3
Arguments
  #self#::Core.Const(ArraysOfArrays.nestedview)
  A::Array{Float64, 3}
  M::Int64
Body::ArrayOfSimilarArrays{Float64, _A, _B, 3, Array{Float64, 3}} where {_A, _B}
...

julia> tc(A) = @cast res[k][i,j] := A[i,j,k];

julia> @code_warntype tc(A)
MethodInstance for tc(::Array{Float64, 3})
  from tc(A) @ Main REPL[482]:1
Arguments
  #self#::Core.Const(tc)
  A::Array{Float64, 3}
Locals
...
Body::Vector{SubArray{Float64, 2, Array{Float64, 3}, Tuple{Base.Slice{Base.OneTo{Int64}}, Base.Slice{Base.OneTo{Int64}}, Int64}, true}}

@mcabbott
Copy link
Owner

mcabbott commented Oct 4, 2022

Maybe worth adding that for tiny arrays, there is also an option to make SMatrix slices, which will often be faster downstream. With perhaps slightly weird notation:

julia> @btime @cast res[k][i,j] := $A[i,j,k];
  min 21.833 μs, mean 78.062 μs (4 allocations, 468.81 KiB)

julia> @btime @cast res[k]{i,j} := $A[i,j,k];  # makes .SMatrix, not type-stable
  min 1.288 μs, mean 1.494 μs (10 allocations, 432 bytes)

julia> @btime @cast res[k]{i:3, j:3} := $A[i,j,k];  # makes .SMatrix of indicated size
  min 24.054 ns, mean 40.098 ns (2 allocations, 80 bytes)

@prittjam
Copy link
Author

prittjam commented Oct 4, 2022

Well here is my use case:



struct AffineBasis{N ,T <: AbstractFloat}
    linear::SMatrix{N,N,T}
    origin::SVector{N,T}
end

    
function world_to_camera(basis::AffineBasis; K=SMatrix{3,3}(1.0I))
    R = basis.linear
    c = basis.origin
    P = K*[R' -R'*c]

    return P
end

R = rand(3,3,100)
X₁ = rand(3,100)

@cast res[k]{i:3, j:3} := R[i,j,k]
@cast Xs[j]{i:3} := X₁[i,j]
bases = StructArray{Affine.AffineBasis}((res,Xs))

Ps = map(world_to_camera,bases)

which confusingly gives

StructVector{SMatrix{3, 4, Float64, 12}, NTuple{12, Vector{Float64}}, Int64} (alias for StructArray{SArray{Tuple{3, 4}, Float64, 2, 12}, 1, NTuple{12, Array{Float64, 1}}, Int64})

so maybe I should just stay away from StructArrrays because I don't understand how its storing contiguous matrices.

@prittjam
Copy link
Author

prittjam commented Oct 4, 2022

Thanks or the responses. I've edited the code snippet to give more context.

@mcabbott mcabbott changed the title TensorCast is 100x slower than ArraysOfArrays (see example) Slicing an array produces Vector{<:SubArray}, hence allocates Oct 6, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants