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
v3 = add(mul(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: 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
rdeits
November 3, 2020, 6:21am
3
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.
lmiq
November 3, 2020, 9:06am
5
Off topic: you can use the splat operator to initialize those vectors:
arnief3:
v1 = Vec3(rand(3)...)
DNF
November 3, 2020, 9:36am
6
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
lmiq
November 3, 2020, 10:33am
7
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).
jishnub
November 3, 2020, 10:44am
8
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
DNF
November 3, 2020, 10:46am
9
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