StaticArrays.jl

repository·master·Indexed 21 days ago

https://github.com/juliaarrays/staticarrays.jl

A Julia framework for implementing statically sized arrays. It provides high-performance implementations of common array and linear algebra operations for small arrays by leveraging compile-time size information. Key types include immutable arrays (SVector, SMatrix, SArray) for stack-based storage and mutable variants (MVector, MMatrix, MArray) for heap-based in-place updates. It also features SizedArray wrappers for standard arrays, FieldVector for custom structs, and the Size trait for size-based dispatch.

Tokens
5.7K
Snippets
24
Records
34
Agent score
68%

What's inside StaticArrays.jl

  1. Perform size-based dispatch using the `Size` trait

    master

    The Size trait provides an abstract representation of a static array's dimensions, allowing the compiler to reason about them. This is useful for performing specialized dispatch based on array dimensions.

    Equivalent ways to construct a Size object:

    • Size{(dims...,)}()
    • Size(dims...)
    • Size(sa::StaticArray)
    • Size(SA) (where SA <: StaticArray)

    Example of size-based dispatch:

    det(x::StaticMatrix) = _det(Size(x), x)
    _det(::Size{(1,1)}, x::StaticMatrix) = x[1,1]
    _det(::Size{(2,2)}, x::StaticMatrix) = x[1,1]*x[2,2] - x[1,2]*x[2,1]
    # Using Size for reshaping or construction
    reshape(svector, Size(2,2))
    SizedMatrix{3,3}(rand(3,3))
  2. Understand the core concept of Static Arrays

    master

    In StaticArrays.jl, a "statically sized" array is one where the size is encoded in the type itself (e.g., StaticArray{Size, T, N}). This allows the compiler to know the dimensions at compile-time.

    Important distinction: "Static" refers to the size being known by the type, but it does not necessarily mean the array is immutable. The package provides both immutable types (like SVector) and mutable types (like MVector).

  3. Understand static vs dynamic indexing

    master

    Indexing behavior depends on the type of the index used:

    • Static Indexing: Indexing with a scalar, a StaticVector, or : results in a statically sized array of the closest type (determined by similar_type).
    • Dynamic Indexing: Indexing a static array with a dynamically sized index (like Vector{Integer} or UnitRange{Integer}) results in a standard, dynamically sized Array.
  4. Use mutable static arrays: `MVector`, `MMatrix`, and `MArray`

    master

    Mutable static arrays (MVector, MMatrix, MArray) allow for in-place updates via setindex!.

    Key Characteristics:

    • Memory: Unlike immutable arrays which live on the stack, mutable static arrays live on the heap and are managed by the garbage collector.
    • Performance: They are faster than Base.Array for small sizes due to reduced pointer indirection and loop unrolling. For optimal speed, use mutating functions (e.g., map!, mul!) to avoid reallocations.
    • Usage Pattern: A common pattern is to build an array iteratively using an MArray and then convert it to an SVector or SMatrix for stack-based use.
    # Efficiently building a static vector
    function standard_basis_vector(T, ::Val{I}, ::Val{N}) where {I,N}
        v = zero(MVector{N,T})
        v[I] = one(T)
        SVector(v)
    end
  5. Choose the right Static Array type

    master

    The package provides several concrete types depending on your needs for mutability and dimensionality:

    Immutable Types (Static)

    Use these for high-performance, fixed-size data that you do not intend to modify in-place:

    • SVector: A static vector.
    • SMatrix: A static matrix.
    • SArray: A general static N-dimensional array.
    • FieldVector: An abstract type used to create fast static vectors out of any uniform Julia struct.

    Mutable Types

    Use these if you need to modify the elements of the array in-place:

    • MVector: A mutable static vector.
    • MMatrix: A mutable static matrix.
    • MArray: A mutable static N-dimensional array.

    Annotation

    • SizedArray: Used for annotating standard Arrays with static size information.
  6. Use SVector, SMatrix, and SArray for static arrays

    master

    StaticArrays provides specialized types for fixed-size arrays that are stored on the stack. The primary types are:

    • SVector{N, T}: A static vector of length N with element type T.
    • SMatrix{R, C, T}: A static matrix with R rows, C columns, and element type T.
    • SArray{Size, T}: A general static array where Size defines the dimensions.

    These types are optimized for small, fixed-size collections where the dimensions are known at compile time.

    using StaticArrays
    
    v = SVector{3, Float64}(1.0, 2.0, 3.0)
    m = SMatrix{2, 2, Int}(1, 2, 3, 4)
  7. Use MVector, MMatrix, and MArray for mutable static arrays

    master

    If you need to modify the elements of a static array in-place, use the mutable variants:

    • MVector{N, T}: A mutable static vector.
    • MMatrix{R, C, T}: A mutable static matrix.
    • MArray{Size, T}: A general mutable static array.
    using StaticArrays
    
    v = MVector{3, Float64}(1.0, 2.0, 3.0)
    v[1] = 10.0
  8. How StaticArrays interact with standard Julia operations

    master

    StaticArrays are designed to be compatible with the AbstractArray interface and standard linear algebra routines:

    • Standard Operations: Supports +, *, sin.(), map, reduce, and broadcasting (broadcast!, etc.).
    • Indexing: Supports standard indexing, slicing (v[:]), and indexing using other static arrays (e.g., v[SVector(3,2,1)]).
    • Linear Algebra: Small matrices (up to 3x3) use specialized algorithms for operations like eigen(). Larger matrices (like MMatrix) are hooked into BLAS/LAPACK.
    • Type Stability: Static arrays stay statically sized even when returned by functions like eigen() or reshape() (using Size).
  9. Convert between `Matrix` and `Vector{SVector}` using `reinterpret`

    master

    A Matrix{T} and a Vector{SVector{N,T}} (where $N$ is the number of rows) have the same binary layout. You can use reinterpret to convert between them without copying data, though the resulting ReinterpretArray has a small runtime penalty on access.

    To avoid the penalty and get a standard Array, use copy(reinterpret(...)).

    using StaticArrays
    
    # Zero-copy conversion (returns ReinterpretArray)
    function svectors(x::Matrix{T}, ::Val{N}) where {T,N}
        size(x,1) == N || error("sizes mismatch")
        isbitstype(T) || error("use for bitstypes only")
        reinterpret(SVector{N,T}, vec(x))
    end
    
    # Conversion with a copy (returns standard Array)
    function svectorscopy(x::Matrix{T}, ::Val{N}) where {T,N}
        size(x,1) == N || error("sizes mismatch")
        isbitstype(T) || error("use for bitstypes only")
        copy(reinterpret(SVector{N,T}, vec(x)))
    end
  10. When to avoid Static Arrays

    master

    Avoid using Static Arrays in the following scenarios:

    1. Dynamic Sizes: If the array size changes frequently at runtime. Recompiling or using dynamic dispatch for every size change is computationally expensive.
    2. Large Arrays: If the array has $\gg 100$ elements. Large arrays cause code size explosion due to unrolling, require heap allocation, and lose the benefits of being stored inline in structs.
    3. Non-Critical Performance: If performance is not a priority, standard Arrays are more convenient because they do not require the size to be encoded in the type.
  11. When to use Static Arrays

    master

    Static Arrays are most effective when working with many small arrays (typically $\lesssim 100$ elements) whose size is fixed at compile-time.

    Benefits

    1. Loop Unrolling: Operations like v3 = v1 + v2 are implemented as a sequence of scalar operations (e.g., 3 additions for a 3-element vector) without loop overhead, often triggering SIMD optimizations.
    2. Allocation Reduction: They avoid heap or stack allocations of temporary arrays by behaving like manual scalar operations.
    3. Inline Storage: They can be stored "inline" within other data structures. For example, a Vector{SVector{3, Float64}} stores $3N$ consecutive Float64 values, making it much more cache-efficient than a Vector{Vector{Float64}} (which is an array of pointers).