I have a similar question concerning a very different use cases: the case where most of the inner vectors are actually equal and pretty large (for example, useful when working with “shifted versions” of the same Array, that can and often will share the underlying data but have different shifts). Making a large Array of views would behave similarly. The usecase is: working with DataFrames where some columns represent a time signal shifted with different shifts.
The situation is the following:
julia> using LinearAlgebra, Random, ShiftedArrays
julia> L = 1:100000
1:100000
julia> v = rand(100000);
julia> using BenchmarkTools
julia> @benchmark [ShiftedArray(v, -i) for i in L]
BenchmarkTools.Trial:
memory estimate: 5.33 MiB
allocs estimate: 199493
--------------
minimum time: 7.076 ms (0.00% GC)
median time: 7.264 ms (0.00% GC)
mean time: 8.692 ms (16.24% GC)
maximum time: 64.684 ms (88.45% GC)
--------------
samples: 576
evals/sample: 1
julia> @benchmark [(v, -i) for i in L]
BenchmarkTools.Trial:
memory estimate: 5.33 MiB
allocs estimate: 199493
--------------
minimum time: 14.498 ms (0.00% GC)
median time: 14.728 ms (0.00% GC)
mean time: 16.352 ms (9.47% GC)
maximum time: 74.507 ms (79.07% GC)
--------------
samples: 306
evals/sample: 1
julia> @benchmark [v for i in L]
BenchmarkTools.Trial:
memory estimate: 781.42 KiB
allocs estimate: 5
--------------
minimum time: 156.358 μs (0.00% GC)
median time: 162.510 μs (0.00% GC)
mean time: 211.878 μs (17.43% GC)
maximum time: 41.303 ms (99.60% GC)
--------------
samples: 10000
evals/sample: 1
julia> @benchmark [-i for i in L]
BenchmarkTools.Trial:
memory estimate: 781.36 KiB
allocs estimate: 3
--------------
minimum time: 46.720 μs (0.00% GC)
median time: 53.779 μs (0.00% GC)
mean time: 74.173 μs (25.85% GC)
maximum time: 40.476 ms (99.87% GC)
--------------
samples: 10000
evals/sample: 1
Though when doing the same with broadcasting, the situation is not so bad in terms of time, but still not ideal in terms of memory:
julia> @benchmark tuple.((v,), L)
BenchmarkTools.Trial:
memory estimate: 3.82 MiB
allocs estimate: 100040
--------------
minimum time: 497.080 μs (0.00% GC)
median time: 538.889 μs (0.00% GC)
mean time: 1.456 ms (62.69% GC)
maximum time: 58.943 ms (99.07% GC)
--------------
samples: 3427
evals/sample: 1
julia> @benchmark ShiftedArray.((v,), L)
BenchmarkTools.Trial:
memory estimate: 3.82 MiB
allocs estimate: 100056
--------------
minimum time: 559.148 μs (0.00% GC)
median time: 645.822 μs (0.00% GC)
mean time: 1.615 ms (59.04% GC)
maximum time: 62.889 ms (99.00% GC)
--------------
samples: 3088
evals/sample: 1
What I don’t understand is the following: the memory estimate and time are massively higher for the case where I store the vector and the shift together (rather than separately).
Does this mean that, for this use case, I need to create a custom ShiftsArray
obect that stores the vectors and the shifts in two separate vectors?
In that case maybe it could also be useful to have a ViewsVector
to do that with views, or even automatize the process that goes from a ArrayOfStructs
to StructOfArrays
(we have that for NamedTuples
already in the IndexableTables package, but I imagine it could be generalized to work with any struct
, not just NamedTuples.