# SVector vs struct performance

I am struggling to understand the huge performance difference I am seeing between these two approaches to creating a Vec3 DataType. I would prefer to use a SVector, but only if the performance is comparable. I feel like I must be missing a critical piece of the puzzle here.

``````
Vec3 = SVector{3,Float64}

function test(v1, v2)
v3 = Vec3(rand(), rand(), rand())
for i = 1:10^7
v3 = (v1 .* v2) .+ v3
end
return v3
end

v1 = Vec3(rand(), rand(), rand())
v2 = Vec3(rand(), rand(), rand())

test(v1, v2)
@time v3 = test(v1, v2)

println(v3)
``````

Result: 2.440927 seconds (40.00 M allocations: 2.384 GiB, 9.92% gc time)

``````struct Vec3
x::Float64
y::Float64
z::Float64
end

add(a::Vec3, b::Vec3)::Vec3 = Vec3(a.x+b.x, a.y+b.y, a.z+b.z)
mul(a::Vec3, b::Vec3)::Vec3 = Vec3(a.x*b.x, a.y*b.y, a.z*b.z)

function test(v1, v2)
v3 = Vec3(0,0,0)
for i = 1:10^7
end
return v3
end

v1 = Vec3(rand(), rand(), rand())
v2 = Vec3(rand(), rand(), rand())

test(v1, v2)
@time v3 = test(v1, v2)

println(v3)
``````

Result: 0.009356 seconds (1 allocation: 32 bytes)

Thanks,
Arnie

1 Like

`const Vec3 = SVector{3,Float64}` fixes this for me. The problem is that without the `const`, `test` can’t know that the definition of `Vec3` won’t change at any time. This makes the whole method type unstable.

5 Likes

Your `Vec3` alias is a non-constant global variable in your first example. Marking that as `const Vec3 = ...` should fix the issue.

This isn’t necessary in your second example because `struct` definitions are always constant and therefore don’t need to be marked `const`

2 Likes

That did the trick! Thank you both for your help.

Off topic: you can use the splat operator to initialize those vectors:

Actually, the above first creates a length-3 standard array, then splats, which can be very expensive.

This is much faster and more convenient:

``````v1 = rand(Vec3)
``````

(>20x as fast and zero allocations on my laptop, this is the same speed as `Vec3(rand(), rand(), rand())`, BTW.)

4 Likes

True, but that does not work for the custom struct Vec3. (And, I agree, even in that case if speed is a concern, splatting is not the best choice).

In this particular example, the struct `Vec3` may subtype `FieldVector` and `rand` will work as expected.

``````julia> struct Vec3{T} <: FieldVector{3,T}
x::T
y::T
z::T
end

julia> rand(Vec3{Float64})
3-element Vec3{Float64} with indices SOneTo(3):
0.8851737855718469
0.47765777403880194
0.6473023269774472
``````

This won’t work in general though.

1 Like

It works if `Vec3` is an alias for `SVector`, which the OP actually wanted to use.

A simple fix for the custom type is

``````julia> Base.rand(::Type{Vec3}) = Vec3(rand(), rand(), rand())

julia> rand(Vec3)
Vec3(0.06672577632910204, 0.7133141256121924, 0.6727958470918942)
``````

but it is a bit more work to get the full `rand` functionality, like e.g. `rand(Vec3, 3, 4)` or setting the rng.

2 Likes