@@ -342,3 +342,82 @@ julia> s
342
342
Foo (44 , " d" )
343
343
Foo (55 , " e" )
344
344
```
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