In How to optimise Julia code: A practical guide, Jakob Nissen suggests that one way to optimize code + reduce allocations is to
- Preallocate buffers once and reuse them multiple times
I’m wondering if someone has a walkthrough of an actual example comparing a “repeatedly allocate” solution to a “preallocate buffers and reuse” solution. I think I understand the idea, but I’m wondering how to follow the advice in practice.
For example, I have a calculation like the following:
struct ResultData
value::Float64
result_type::Bool
end
function calculate_and_use(x, y, z)
if y > z
temp = ResultData(y + z, true)
else
temp = ResultData(y * z, false)
end
if temp.result_type
x + z
else
x - z
end
end
Please ignore the implementation details—calculate_and_use
is any involved calculation that involves an allocation of an object, here played by ResultData
, which has some overhead.
Let’s say calculate_and_use
has to be called millions of times by some function, so each time there’s an allocation of a ResultData
object which is then thrown away and, voila, re-allocated a split second later. (Not that this seems to matter, but ResultData
could actually be a fixed-length Vector{ResultData}
or similar, but in any case: it’s created, used, and then thrown away.)
What would be the analogue of pre-allocating a buffer in this context? Would I have some global buf = ResultData(0.0,false)
that just gets overwritten? (But this violates “Use immutable structs over mutable struct
when mutation is not needed,” as well as violating the dictum not to use global
s.)
If not, then what is the right way to follow the preallocation advice in this context?
Or have I misunderstood the preallocation advice…and it is only applicable in other contexts?
Any suggestions appreciated; thank you!