@@ -342,3 +342,82 @@ julia> s
342342 Foo (44 , " d" )
343343 Foo (55 , " e" )
344344```
345+
346+ ## Advanced: StructArrays versus struct-of-arrays layout in higher-dimensional array
347+
348+ Regular arrays of structs can sometimes be reinterpreted as arrays of primitive values with an added
349+ initial dimension.
350+
351+ ``` julia
352+ julia> v = [1.0 + 3im , 2.0 - im]
353+ 2 - element Vector{ComplexF64}:
354+ 1.0 + 3.0im
355+ 2.0 - 1.0im
356+
357+ julia> reinterpret (reshape, Float64, v)
358+ 2 × 2 reinterpret (reshape, Float64, :: Vector{ComplexF64} ) with eltype Float64:
359+ 1.0 2.0
360+ 3.0 - 1.0
361+ ```
362+
363+ However, the situation is more complex for the ` StructArray ` format, where ` s = StructArray(v) ` is
364+ stored as two separate ` Vector{Float64} ` . ` reinterpret ` on ` StructArray ` returns an
365+ "array-of-structs" layout, as the reinterpretation works element-wise:
366+
367+ ``` julia
368+ julia> s = StructArray ([1.0 + 3im , 2.0 - im])
369+ 2 - element StructArray (:: Vector{Float64} , :: Vector{Float64} ) with eltype ComplexF64:
370+ 1.0 + 1.0im
371+ 2.0 - 1.0im
372+
373+ julia> reinterpret (reshape, Float64, s) # The actual memory is `([1.0, 2.0], [3.0, -1.0])`
374+ 2 × 2 reinterpret (reshape, Float64, StructArray (:: Vector{Float64} , :: Vector{Float64} )) with eltype Float64:
375+ 1.0 2.0
376+ 3.0 - 1.0
377+ ```
378+
379+ If you already have a ` StructArray ` , the easiest way is to get the higher-dimensional
380+ "struct-of-arrays" layout is to directly stack the components in memory order:
381+
382+ ``` julia
383+ julia> using StackViews # lazily cat/stack arrays in a new tailing dimension
384+
385+ julia> StackView (StructArrays. components (s)... )
386+ 2 × 2 StackView{Float64, 2 , 2 , Tuple{Vector{Float64}, Vector{Float64}}}:
387+ 1.0 3.0
388+ 2.0 - 1.0
389+ ```
390+
391+ StructArrays also provides ` dims ` keyword to reinterpret a given memory block without creating new
392+ memory:
393+
394+ ``` julia
395+ julia> v = Float64[1 3 ; 2 - 1 ]
396+ 2 × 2 Matrix{Float64}:
397+ 1.0 3.0
398+ 2.0 - 1.0
399+
400+ julia> s = StructArray {ComplexF64} (v, dims= 1 )
401+ 2 - element StructArray (view (:: Matrix{Float64} , 1 , :), view (:: Matrix{Float64} , 2 , :)) with eltype ComplexF64:
402+ 1.0 + 2.0im
403+ 3.0 - 1.0im
404+
405+ julia> s = StructArray {ComplexF64} (v, dims= 2 )
406+ 2 - element StructArray (view (:: Matrix{Float64} , :, 1 ), view (:: Matrix{Float64} , :, 2 )) with eltype ComplexF64:
407+ 1.0 + 3.0im
408+ 2.0 - 1.0im
409+
410+ julia> s[1 ] = 0 + 0im ; s # `s` is a reinterpretation view and doesn't copy memory
411+ 2 - element StructArray (view (:: Matrix{Float64} , :, 1 ), view (:: Matrix{Float64} , :, 2 )) with eltype ComplexF64:
412+ 0.0 + 0.0im
413+ 2.0 - 1.0im
414+
415+ julia> v # thus `v` will be modified as well
416+ 2 × 2 Matrix{Float64}:
417+ 0.0 0.0
418+ 2.0 - 1.0
419+ ```
420+
421+ For column-major arrays, reinterpreting along the last dimension (` dims=ndims(v) ` ) makes every
422+ component of ` s ` a view of contiguous memory and thus is more efficient. In the previous example,
423+ when ` dims=2 ` we have ` s.re == [1.0, 2.0] ` , which reflects the first column of ` v ` .
0 commit comments