Here is a benchmark of various versions for different lengths.
The TL;DR seems to be: Prefer MVector
s over SVector
s if you need to modify an argument a lot. This goes against what I thought I “knew”. As @marius311 suggested, this may have changed in a recent version of Julia.
It seems that creating SVector
s can end up being expensive; I am guessing that this is when actual memory copying is occuring?
using StaticArrays
using BenchmarkTools
"Uses setindex"
function act_on_components1(f, v::StaticArray, indices)
v2 = getindex.(Ref(v), indices)
result = f(v2)
for i in 1:length(indices)
@inbounds v = setindex(v, result[i], indices[i])
end
return v
end
"Converts to MArray and back"
function act_on_components2(f, v::StaticArray, indices)
v2 = getindex.((v,), indices)
w = f(v2)
mv = MArray(v)
@inbounds for (i, idx) in enumerate(indices)
mv[idx] = w[i]
end
return SArray(mv)
end
"Modifies in place"
function act_on_components!(f, v, indices)
v2 = getindex.((v,), indices)
w = f(v2)
@inbounds for (i, idx) in enumerate(indices)
v[idx] = w[i]
end
return v
end
function run_benchmark(f, v, indices)
println()
@show length(v)
println("SVector using setindex:")
v1 = SVector(v...)
@btime act_on_components1($f, v, $indices) setup=(v = $v1);
println("SVector using MVector:")
@btime act_on_components2($f, v, $indices) setup=(v = $v1);
println("In-place MVector:")
v2 = MVector(v...)
@btime act_on_components!($f, v, $indices) setup=(v = $v2);
println("In-place Vector")
v3 = [v...]
@btime act_on_components!($f, v, $indices) setup=(v = $v3);
end
f( (x, y, z) ) = (x + y, y + z, z + x)
run_benchmark(f, 1:10, (1, 3, 7));
run_benchmark(f, 1:20, (1, 3, 7));
run_benchmark(f, 1:50, (5, 20, 49));
run_benchmark(f, 1:100, (5, 20, 99));
run_benchmark(f, 1:200, (5, 20, 199));
run_benchmark(f, 1:500, (5, 20, 499));
using IntervalArithmetic
println()
println("With intervals:")
run_benchmark(f, interval.(1:10), (1, 3, 7));
run_benchmark(f, interval.(1:20), (1, 3, 7));
run_benchmark(f, interval.(1:50), (5, 20, 49));
run_benchmark(f, interval.(1:100), (5, 20, 99));
run_benchmark(f, interval.(1:200), (5, 20, 199));
run_benchmark(f, interval.(1:500), (5, 20, 499));
Results:
length(v) = 10
SVector using setindex:
5.495 ns (0 allocations: 0 bytes)
SVector using MVector:
6.991 ns (0 allocations: 0 bytes)
In-place MVector:
2.911 ns (0 allocations: 0 bytes)
In-place Vector
2.760 ns (0 allocations: 0 bytes)
length(v) = 20
SVector using setindex:
9.502 ns (0 allocations: 0 bytes)
SVector using MVector:
8.036 ns (0 allocations: 0 bytes)
In-place MVector:
2.767 ns (0 allocations: 0 bytes)
In-place Vector
2.760 ns (0 allocations: 0 bytes)
length(v) = 50
SVector using setindex:
68.280 ns (0 allocations: 0 bytes)
SVector using MVector:
10.691 ns (0 allocations: 0 bytes)
In-place MVector:
3.066 ns (0 allocations: 0 bytes)
In-place Vector
2.825 ns (0 allocations: 0 bytes)
length(v) = 100
SVector using setindex:
161.644 ns (0 allocations: 0 bytes)
SVector using MVector:
28.377 ns (0 allocations: 0 bytes)
In-place MVector:
2.998 ns (0 allocations: 0 bytes)
In-place Vector
2.770 ns (0 allocations: 0 bytes)
length(v) = 200
SVector using setindex:
290.125 ns (0 allocations: 0 bytes)
SVector using MVector:
58.724 ns (0 allocations: 0 bytes)
In-place MVector:
2.546 ns (0 allocations: 0 bytes)
In-place Vector
2.315 ns (0 allocations: 0 bytes)
length(v) = 500
SVector using setindex:
1.047 ÎĽs (0 allocations: 0 bytes)
SVector using MVector:
137.492 ns (0 allocations: 0 bytes)
In-place MVector:
2.600 ns (0 allocations: 0 bytes)
In-place Vector
2.841 ns (0 allocations: 0 bytes)
With intervals:
length(v) = 10
SVector using setindex:
50.375 ns (0 allocations: 0 bytes)
SVector using MVector:
24.940 ns (0 allocations: 0 bytes)
In-place MVector:
19.855 ns (0 allocations: 0 bytes)
In-place Vector
19.640 ns (0 allocations: 0 bytes)
length(v) = 20
SVector using setindex:
124.472 ns (0 allocations: 0 bytes)
SVector using MVector:
29.527 ns (0 allocations: 0 bytes)
In-place MVector:
21.473 ns (0 allocations: 0 bytes)
In-place Vector
21.005 ns (0 allocations: 0 bytes)
length(v) = 50
SVector using setindex:
305.090 ns (0 allocations: 0 bytes)
SVector using MVector:
48.689 ns (0 allocations: 0 bytes)
In-place MVector:
20.079 ns (0 allocations: 0 bytes)
In-place Vector
20.070 ns (0 allocations: 0 bytes)
length(v) = 100
SVector using setindex:
635.631 ns (0 allocations: 0 bytes)
SVector using MVector:
77.139 ns (0 allocations: 0 bytes)
In-place MVector:
21.217 ns (0 allocations: 0 bytes)
In-place Vector
19.850 ns (0 allocations: 0 bytes)
length(v) = 200
SVector using setindex:
1.528 ÎĽs (0 allocations: 0 bytes)
SVector using MVector:
147.505 ns (0 allocations: 0 bytes)
In-place MVector:
19.644 ns (0 allocations: 0 bytes)
In-place Vector
20.753 ns (0 allocations: 0 bytes)
length(v) = 500
SVector using setindex:
4.519 ÎĽs (0 allocations: 0 bytes)
SVector using MVector:
1.452 ÎĽs (0 allocations: 0 bytes)
In-place MVector:
20.529 ns (0 allocations: 0 bytes)
In-place Vector
20.752 ns (0 allocations: 0 bytes)