# What is the maximum inlining depth in practice?

Out of professional curiosity: What is the maximum depth for inlining function calls in Julia in practice?

My programming intuition says that there must exist a practical limit regarding how deeply nested functions may be before they are not inlined anymore. Is this a fixed (possibly configurable) number? Or does it depend on some compiler heuristics? And if yes, what are the limits in practice that people have experienced?

I am asking since Julia favors many small functions and we’ve tried to do this in Trixi.jl. However, people coming from other languages (ahem C++ or Fortran ahem) still cringe when they see it, and I’d like to back my claim that small functions make for fast code with some hard numbers during the next discussion

3 Likes

As mentioned on Slack, I generated 52 functions each which calls the previous, where the final step calls `sum` (which itself has a call stack 13 functions deep), and that all inlined.

So if there is a limit, it’s at least 66 layers deep - and will probably never be reached in practice.

1 Like

Thanks for your answer! Out of curiosity: How did you generate those functions and how were you able to determine that everything is inlined?

I deleted the code, but it was something along the lines of

``````@inline a(x) = sum(x)
for i in 'b':'z'
@eval @inline \$(Symbol(i))(foo) = \$(Symbol(i - 1))(foo)
end
``````

I also added another loop with the capital letters. It’s easy to extend this as far as you want. To determine whether it inlined, I did
`@code_native z([1])`

Thanks!

You could use numbers to make reaching arbitrary depths easier:

``````julia> @inline a_0(x) = sum(x)
a_0 (generic function with 1 method)

julia> for i in 1:200
@eval @inline \$(Symbol(:a_, i))(x) = \$(Symbol(:a_, i-1))(x)
end

julia> @code_native debuginfo=:none syntax=:intel a_200((1.0,2.0))
.text
vmovsd  xmm0, qword ptr [rdi]           # xmm0 = mem[0],zero
vaddsd  xmm0, xmm0, qword ptr [rdi + 8]
ret
nop     word ptr [rax + rax]
``````
``````; julia> @code_llvm debuginfo=:none a_200((1.0,2.0))
define double @julia_a_200_1697([2 x double]* nocapture nonnull readonly align 8 dereferenceable(16) %0) {
top:
%1 = getelementptr inbounds [2 x double], [2 x double]* %0, i64 0, i64 0
%2 = getelementptr inbounds [2 x double], [2 x double]* %0, i64 0, i64 1
%3 = load double, double* %1, align 8
%4 = load double, double* %2, align 8
%5 = fadd double %3, %4
ret double %5
}
``````

In practice, you can hit non-specializing heuristics if you’re not careful.

Do you mean this warning?

Yes. I believe I also hit it using pass-through singleton types, but managed to avoid it via reconstructing them in each layer.