-
-
Notifications
You must be signed in to change notification settings - Fork 5.7k
Description
This is best explained by an example.
mutable struct Tester{F,T}
f::F
x::T
y::T
end
t = Tester((x,y)->2x+2y,1.0,1.0)
function stepper!(t)
t.x = t.f(t.x,t.y)
end
function other_stuff!(t)
t.y += 1
end
function looper(t)
for i in 1:10
stepper!(t)
other_stuff!(t)
end
end
looper(t)
tIn this setup, both other_stuff! and stepper! will specialize on every type parameter change {F,T}. However, let's say that F changes often and T doesn't change often. Then both functions will recompile every time when only stepper! needs to. Thus compilation time is increased.
A user can work around it by pulling apart the pieces of the type they need:
function other_stuff2(y)
y += 1
end
function looper2(t)
for i in 1:10
stepper!(t)
t.y = other_stuff2(t.y)
end
end
looper2(t)here other_stuff2 only takes in a value which has type T, so if T is unchanged then the same compiled code can be re-used (if it doesn't inline and all of that). However, as the operations in other_stuff! get more complicated, the complexity of the function signature and the return that are required to make this work grows.
I see this as a common pattern in scientific computing libraries like DiffEq, Optim, etc. since the code for the general algorithms is much larger in comparison to the code that actually has to touch function calls. The larger algorithm has convergence checks, output generation, etc. that are usually always the same (since there T would just be Float64 which most users would be using). However, the entire function stacks are recompiling each time due to the over-specialization on unused parts of types in the other_stuff! functions. From tests it seems that F specialization can be a big deal for many use cases (as much as 2x-4x in comparison to FunctionWrappers.jl in some real cases!) so that cannot be easily dropped, but fully decomposing every "non-f function" is quite laborious.
The nice option would be for the compiler to recognize this is the case and just not specialize on these functions. Another option would be to allow @nospecialize on type parameters.