I’m confused by why the following allocates (this is a reduced example that arose from trying to implement a function from #32739 via higher order functions instead of explicit loops):
I guess that suggests that the allocations are not really due to mapreduce, foldl, sum, or similar, but to the anonymous function itself? Still, I don’t really get why this happens.
Eliminating both of these elements removes the allocation noted above; but getting rid of recursion essentially butchers the method.
[It seems that recursion and zero allocations can co-exist, at least in principle, if specialization on the function input is forced via mapreduce_impl(f::F, ...) where F<:Function as suggested in #18207(comment); that’s probably not very viable for such a generic function thoughScratch that; doesn’t actually seem to work for me.]
I actually think that the root cause here is the fact that a closure over a non-isbits value (v) is created and subsequently passed to a non-inlined function (mapreduce or one of the functions it calls with the closure as an argument, recursively). The compiler essentially treats the function as a black box (or at least more so than if the function were inlined), and can’t figure out that the closure is short-lived; for all it knows, mapreduce could store a reference to the closure somewhere in a global array of objects. This means that the closure needs to be GC-rooted, which requires allocation.