Repository navigation
Conversation
|
This might be too specific for FSA, and perhaps we can just introduce a |
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #210 +/- ##
==========================================
+ Coverage 93.50% 95.41% +1.90%
==========================================
Files 6 10 +4
Lines 231 349 +118
==========================================
+ Hits 216 333 +117
- Misses 15 16 +1 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
|
Other people expressed before the interest in having a static array which is backed by Memory and not tuple, so that you don't pay the penalty for large arrays (and smaller ones may still be allocated on the stack as the compiler gets better), so this is cool! |
The generic `fill!` does not elide the bounds checks for `FixedSizeArray`s on Julia v1.13 and later, so it does not vectorize (#200). Looping over the memory backend is as fast as or faster than both the generic method and forwarding to `fill!` of the memory backend on Julia v1.10 to v1.14-DEV (the latter is slower for short arrays on v1.13+). Also enable the corresponding FileCheck test for `FixedSizeArray`s again, and skip the one for views also on prereleases of v1.13. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MEJYUviAUshj5fZt8LTe8A
New public (not exported) type `StaticSizeArray{T,N,D,Mem}`: an `N`-dimensional
array over a `DenseVector` memory backend whose dimensions are a tuple of type `D`
of `Int`s and Static.jl `StaticInt`s, e.g. `(static(5), static(4), n)`. Any
dimension can be static (a compile-time constant that takes no space) or dynamic
(set at runtime), as in StrideArraysCore and JuliaGPU/GPUArrays.jl#814.
* constructors: `wrap(StaticSizeArray, mem, dims)` (with at most one dimension
given as `:`, computed from the length of `mem`), `undef` constructors, and
`StaticSizeArray(a, dims)` sharing the memory of a `FixedSizeArray`
* fast linear indexing and Cartesian indexing as a sum of strided terms, which
are constants as long as the dimensions before them are static,
`LightBoundsError`s, `size(a, d)` is a constant for static dimensions
* strided arrays interface, `similar` (a `StaticSizeArray` without a size or
from the type, a `FixedSizeArray` with a size, so that range indexing and
reductions infer), `copy`, `fill!`, `copyto!` forwarding to the memory,
broadcasting via `FixedSizeArrayBroadcastStyle` (also for views),
`reshape`/`vec` returning `FixedSizeArray`s
* extensions: Adapt (adapting the memory backend, for any backend), MPI (as
communication buffer), and the new StaticArrayInterface extension (static sizes
and strides for StrideArraysCore, LoopVectorization and Polyester; `device`
of the memory backend, also for `FixedSizeArray`s)
* docs: motivation and a comparison with StaticArrays, HybridArrays and
StrideArraysCore
* the methods which only depend on the memory backend (linear indexing,
`iterate`, `fill!`, `copyto!`, `reshape`, pointers, ...) are shared with
`FixedSizeArray`s via the internal `FixedOrStaticSizeArray` union
Static.jl is a new dependency.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
a83c387 to
4c37971
Compare
|
I'm happy about the feature, but the I'm a bit uncomfortable about the hard dependency on Static with its invalidations |
Loading Static.jl increased the load time of `using FixedSizeArrays` from about 10 ms to 48 ms and added many invalidations, for every user. Now the code in `src` handles the dimensions of a `StaticSizeArray` only through the internal functions `dim_value` and `known_dim` (and `to_static_dim` and `convert_dim`), with methods for `Int`s, and the new package extension `FixedSizeArraysStaticExt` adds their methods for `StaticInt`s. Static dimensions are available once Static.jl is loaded; without it, all dimensions are `Int`s. The StaticArrayInterface extension now also depends on Static.jl (which StaticArrayInterface.jl loads anyway) and computes the `StaticInt` strides itself. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This PR adds the public (not exported) type
FixedSizeArrays.StaticSizeArray{T,N,D,Mem}: anN-dimensional array over a memory backendMem<:DenseVector{T}whose dimensions are a tuple of typeDofInts and Static.jlStaticInts, e.g.(static(5), static(4), static(4), nelements). Any dimension can be static (a compile-time constant that takes no space in the array) or dynamic (set at runtime). The design follows StrideArraysCore.jl and JuliaGPU/GPUArrays.jl#814.It also includes a fix for #200:
fill!(::FixedSizeArray, x)loops directly over the memory backend, so that it elides the bounds checks and vectorizes on Julia v1.13+.Motivation
In Trixi.jl the solution is stored as an array of size
(nvariables, nnodes, ..., nelements). The number of variables and nodes is fixed by the equations and the polynomial degree, while the number of elements depends on the mesh and changes at runtime (e.g. with AMR). With static dimensions, the compiler folds the index computations into constants and can unroll the loops over the small dimensions. This matters in particular for:@batchand LoopVectorization.jl: captured arrays are converted toPtrArrays through StaticArrayInterface.jl. With a plainFixedSizeArrayall sizes become dynamic; with aStaticSizeArray,static_sizeandstatic_strideskeep the static dimensions static.isbitswhen its memory backend is, and the strides of the static dimensions are constants. (Have to revisit this for Add StaticDimsArray: GPU arrays with a mix of static and dynamic dimensions JuliaGPU/GPUArrays.jl#814, this here might be sufficient to get static type information down to kernels)Design
memanddims. AStaticIntindimstakes no space, so e.g. a 3-D array with two static dimensions is the size of aFixedSizeVector.FixedSizeArrays.wrap(StaticSizeArray, mem, dims), where at most one dimension can be:and is computed fromlength(mem), e.g. to wrap aVectoragain afterresize!;undefconstructors, e.g.StaticSizeArray{T}(undef, dims); when the type gives the dimensions, the static ones can be passed as plainInts, sosimilar(typeof(a), size)works;StaticSizeArray(a::FixedSizeArray, dims), sharing the memory.IndexLinear. The Cartesian linear index is a sum of strided terms, whose strides are constants as long as the dimensions before them are static. Unlike the Horner form, this lets loop-invariant terms be hoisted when dimensions are dynamic; with all leading dimensions static, both forms perform the same.size(a, d)is a compile-time constant for a static dimension.strides,elsize,unsafe_convert),copy,fill!,copyto!;similar(a)andsimilar(a, T)returnStaticSizeArrays, whilesimilar(a, T, size)returns aFixedSizeArray, so that range indexing and reductions infer;reshapeandvecreturnFixedSizeArrays.iterate,fill!,copyto!,reshape, pointers,dataids) are shared withFixedSizeArraythrough an internalFixedOrStaticSizeArrayunion.static_size,static_strides,known_size,known_strides.StaticArrayInterface.deviceof aFixedSizeArrayorStaticSizeArrayis forwarded to its memory backend, so that e.g. LoopVectorization.jl does not treat a GPU memory backend as a CPU pointer.StaticSizeArrayand wraps it again, for any memory backend.StaticSizeArrays can be used as communication buffers.Array,FixedSizeArray, StaticArrays.jl, HybridArrays.jl and StrideArraysCore.jl, plus the reference and the CHANGELOG.Open question: Static.jl as a hard dependency
Static.jl is a new hard dependency, but only
StaticInt,dynamicandknownare used. Loading it increases the load time ofusing FixedSizeArraysfrom about 10 ms to 48 ms and adds many invalidations, for every user. JuliaGPU/GPUArrays.jl#814 has the same open question. The alternatives are:StaticSizeArrayinto a package extension on Static.jl;StaticInthere and converting to and from Static.jl in an extension.Testing
All tests pass on Julia v1.10 and v1.12, including Aqua.jl and the doctests. The new FileCheck tests check that indexing with static dimensions uses constant strides, that
fill!vectorizes, and that in-place broadcasting is vectorized and free of bounds checks. They run only without--check-bounds=yes. Run that way, three existing heap-allocation FileCheck tests fail the same way onmain.This PR was written with major contributions from Claude Code (an AI assistant) and reviewed by me.
🤖 Generated with Claude Code