I am currently using GeometryBasics.jl for a project. While optimizing the code, I noticed lots of memory allocations that hinders performance. Some of them I could figure it out but this one I cannot. Can somebody help me understanding why in the following test code there is one allocation. The code is really trivial and I was expecting no allocation. I am using version 1.6.2.
julia> using GeometryBasics, BenchmarkTools
julia> function tt2(p::Point3)
p[1]*p[1] + p[2]*p[2] + p[3]*p[3]
end
tt2 (generic function with 1 method)
julia> a = Point3{Float64}(1,1,1)
3-element Point3{Float64} with indices SOneTo(3):
1.0
1.0
1.0
julia> @btime tt2(a)
20.325 ns (1 allocation: 16 bytes)
3.0
Interpolate your variables into benchmark tools macros, with $:
@btime tt2($a)
Your tta funciton is just math on Float64 - there’s nothing to allocate. So it’s the global variable that is causing those allocations. Interpolating the variable moves it to the scope of the benchmark.
Now that I understand that just math functions should not allocate, I found that looping on existing and preallocated data structures does allocate. See the example:
using GeometryBasics, BenchmarkTools
function mod2(p::Point3)
p[1]*p[1] + p[2]*p[2] + p[3]*p[3]
end
struct Triangle
a::Point3
b::Point3
c::Point3
end
function mod2(t::Triangle)
mod2(t.a) + mod2(t.b) + mod2(t.c)
end
struct Polygone
triangles::Vector{Triangle}
end
function mod2(p::Polygone)
sum = 0.
for t in p.triangles
sum += mod2(t)
end
sum
end
a = Point3{Float64}(0,0,0)
b = Point3{Float64}(1,0,0)
c = Point3{Float64}(0,1,0)
d = Point3{Float64}(0,0,1)
p = Polygone([Triangle(a,b,c),Triangle(a,b,d),Triangle(a,c,d), Triangle(b,c,d)])
@btime mod2($p)
374.458 ns (20 allocations: 320 bytes)
9.0
Is there a way to avoid these allocations? Thanks.
If something simple like that is allocating, its usually because your code isn’t type stable. To add to oscars example, also Polygonetriangles field will still have the abstract type Vector{Triangle}
Use a type parameter instead of an abstract typed field:
struct Polygone{T<:Vector{<:Triangle}}
triangles::T
end
Or:
struct Polygone{T<:Triangle}}
triangles::Vector{T}
end
You can check type stability of your funciton calls with the @code_warntype macro.