Hi,
I thought I got better at this, but there’s a function that’s creating allocations and I can’t figure out why. I’ve spent a few hours troubleshooting this with no success. Here’s the function:
using StaticArrays
function transformPoint(point,transformationMatrix)
homogeneousPoint = SA_F64[point[1],point[2],point[3],1.0];
homogeneousPoint = transformationMatrix*homogeneousPoint;
return SA_F64[homogeneousPoint[1],homogeneousPoint[2],homogeneousPoint[3]];
end
where the inputs are a SVector of length 3 and a 4x4 SMatrix. For instance:
julia> @btime transformPoint($point, $transformationMatrix)
0.017 ns (0 allocations: 0 bytes)
3-element SVector{3, Float64} with indices SOneTo(3):
18.0
46.0
18.0
Note that the sub-nanosecond timing means that the compiler has optimized away the entire function, which is pretty neat. We can avoid that by hiding the input behind a Ref:
Yeah, when I first wrote the function and tested it in isolation, it wasn’t. But when I run it within another function I get allocations.
I’m having a hard time reproducing it, but this seems to do it:
As far as I can tell, both of these allocate even worse than mine. My experience with Julia is that fancy short code typically allocates (at least at first) and ugly code (like my function) does not. I avoid splatting and so on at all costs.
No, they don’t allocate anything, the problem is your benchmarking. You cannot use @time for this. Use BenchmarkTools.jl.
No, no, no. This is way off. Write short, elegant code, and splat static arrays and tuples to your heart’s content. Just make sure that you do proper benchmarking with BenchmarkTools.
You’re right, but now I can’t reproduce my issue at all. My function is crazy slow at the moment and I have no idea why. I thought it was this part, but maybe it’s something else altogether. I benchmark it with @btime and see a ton of allocations, but I don’t know where they are coming from. Any ideas on troubleshooting this? I tried @profview, but it just showed that the code was on my main function for a long time, doing nothing (it seemed).
Wish I could, but typically that makes the process 50x slower, as I write nice looking code and then spend a day trying to fix allocations. I need to get smarter before I write pretty functions =.
I take it back. It was easy to debug. I just commented out the lines with transformPoint (so the code is not doing the right thing, but it is running) and a crap ton of allocations went away. So transformPoint is the issue, but I don’t know why.
That was my first instinct, but @code_warntype shows everything blue.
This is what the function looks like:
function myfun!(bodies,solver)
if solver.condition
...
else # this is where we are
Threads.@threads for k in 1:M
point₁,point₂,point₃ = ...
for j in 1:N
...
for b in 1:B
transformationMatrix = solver.Matrices[b];
point₁ = transformPoint(point₁,transformationMatrix);
point₂ = transformPoint(point₂,transformationMatrix);
point₃ = transformPoint(point₃,transformationMatrix);
...
end
...
end
end
end
...
return nothing;
end
Unfortunately I can’t easily share something easy to reproduce.
Check @code_warntype without @threads since @threads will hide its body since it gets pulled out in a closure
julia> function f()
Threads.@threads for i in 1:5
x = rand() > 0.5 ? 1 : "1" # type unstable
end
end
f (generic function with 1 method)
julia> @code_warntype f() # does not show any type instability
...
julia> function g()
for i in 1:5
x = rand() > 0.5 ? 1 : "1"
end
end
g (generic function with 1 method)
julia> @code_warntype g() # shows type unstable
Variables
#self#::Core.Const(g)
@_2::Union{Nothing, Tuple{Int64, Int64}}
i::Int64
x::Union{Int64, String}
@_5::Union{Int64, String}
Thanks a lot! This finally pointed me towards the issue, which as @DNF assumed, is a type instability problem.
Basically, my matrix was defined as Vector{StaticArrays.SMatrix{4,4,Float64}}, which is not good enough, apparently. I switched to Vector{StaticArrays.SMatrix{4,4,Float64,16}} and it works!
Thanks again, everyone!