I get that the compiler cannot figure out the type of vs[1:3], but if I could tell it to “drop the last element of vs” somehow, then it would know to the return type SVector{3}.
I also need to do this for SVector{3}, SMatrix{4,4} and SMatrix{3,3}. I can of course write several methods like this:
That’s excellent! I’m still missing a step, though. Do you know how I could create an index vector 1:N-1 to be able to drop the last index for a size N vector. I tried this:
function droplast(v::SVector{N, <:Number}) where {N}
ind = @SVector [i for i in 1:N-1]
return v[ind]
end
but this looks for N in global scope, so doesn’t work. Interestingly, @SVector fill(1, N-1) and some others behave nicely inside functions, so perhaps adding something like @SVector 1:N-1 could be a feature request to StaticArrays.jl?
For now, perhaps a @generated function would do the trick?
julia> Base.@pure pure_sub(a::Int, b::Int) = a - b
pure_sub (generic function with 1 method)
julia> function droplast(v::SVector{N, <:Number}) where {N}
ind = SVector{pure_sub(N, 1)}(1:N-1)
return v[ind]
end
droplast (generic function with 1 method)
julia> @btime droplast($a)
6.216 ns (0 allocations: 0 bytes)
4-element SVector{4,Float64}:
0.945968
0.0451724
0.917798
0.368511
julia> droplast(x::Tuple) = argdroplast(x...)
droplast (generic function with 1 method)
julia> argdroplast(x) = ()
argdroplast (generic function with 1 method)
julia> argdroplast(x,y...) = (x,argdroplast(y...)...)
argdroplast (generic function with 2 methods)
julia> droplast(v::SVector) = SVector(droplast(v.data))
droplast (generic function with 2 methods)
Looks like the generated one and tuple is the same speed except for n > 15 when the tuple one gives up. Pure is slower but doesn’t freak out at n = 15.
Sorry to revive an old topic, but it isn’t clear from the docs if what’s suggested here is still the best approach. Have there been any updates in the last years?
I don’t have a solution for everything, but pop(v) drops the last element, and there’s an SOneTo which is a static version of the Base.OneTo range, which can be useful.
EDIT: benchmarks in a lower post indicate that SizedVector rarely offers a performance advantage over SVector and is sometimes worse.
The nicest way to take slices is with SizedArray. Like
julia> using StaticArrays
julia> x = (1:5).^2 # note: this doesn't even need to be a StaticArrays object
5-element Vector{Int64}:
1
4
9
16
25
julia> x[SizedVector{3}(1:2:5)] # indexing with SizedArray returns a SArray
3-element SVector{3, Int64} with indices SOneTo(3):
1
9
25
SOneTo can also work, but can only grab the front of an array with stride=1.
I’m pretty sure that operations on SizedVector are still usually unrolled so carry the same high compiler burden at large sizes. The @code_native I looked at below corroborated this. I don’t think they would have much purpose otherwise.
Mostly, I would expect SizedVector to be preferable when a range object is appreciably more compact than a SVector. So I would reach for SVector{3}(1:3) but SizedVector{10}(1:10). It may also be easier for the compiler to reason about inbounds access and other factors. But, by and large, they aren’t so different for most methods.
Here, we see that a medium-sized SVector was a bit faster for slicing an array than a SizedVector. But the SizedVector was faster for sequential iteration. But SVector could be vectorized more easily.
Although different benchmarks might tell a different story in certain applications, it looks like SArray is probably preferable in most situations even at larger sizes. In some distant future of fantasy, maybe more effort goes into SizedArray (it could be smarter about some things, probably) and it has more situations where it’s useful. For example, @code_native x[SVector{5}(1:5)] and @code_native x[SizedVector{5}(1:5)] both have 5 boundschecks (and the SizedVector error throwing appears more complicated, for some reason I haven’t looked into), even though the SizedVector{N,T,UnitRange{T}} indexing could be done with 2 (for any size).
If nothing else, if it’s wrapping some compact object (like a range) then it takes up notably less memory. Not a big deal when you only have a couple arrays, but maybe when you have a bunch. I’d probably use a Vector{SizedVector} over a Vector{SVector} where the former could be done with compact representations.
Two things mostly. Storing non-isbits objects and adding size information to arbitrary AbstractArrays like views (or ranges). You can view a part of MArray and operate on it freely, it will be fast in most cases.
SizedArrays shouldn’t be larger than 100 elements either because there is almost no unrolling avoidance in StaticArrays.jl. It’s not particularly difficult to prevent some common unrolling cases but in practice it’s even easier to work around the problem by not using static arrays for larger arrays.