I’m looking into redesigning some code I wrote long ago which implements a strategy pattern. A mockup example:
struct StrategyX{T1,T2, V} <: AbstractStrategy
strategyparam1::T1
strategyparam2::T2
applyto::V
end
step1!(s::StrategyX, problem) = setsomething!(problem[s.applyto], s.strategyparam1 + property1(s.applyto))
step2!(s::StrategyX, problem) = # apply step 2 to problem
#many more steps and strategies...
# Main function looks something like this
function dothething!(strategy, userinput::Vector)
problem = createproblem(strategy, userinput) # Calls steps above
result = solve(problem)
for e in userinput
applyresult!(e, result[e])
end
The concrete concern is that applyto
is user created and the set of types it can be of can be of is quite large. This in turn tends to cause the latency to be dominated by compile time as dothething!
is typically applied repeatedly with different input.
I’m planning to take SnoopCompile for a spin to profile, but while I try to find the time to do so I’d like to know if there are any tools or patterns which may be useful to reduce the latency.
Here are some I can think of:
- Use
@nospecialize
. Can it be applied to parametric types (e.g. something likestep1(s::StrategyX{T1, T2, @nospecialize(V)}
? - Take the type of
applyto
off the struct definitions. Will this give the exact same behaviour as@nospecialize
(for functions where it is used)? - Extract simpler types from the user input (e.g.
property1
) and put only what is needed in the strategy structs and problem struct (e.g. use integers as keys instead).
I guess that out of those nr 3 is has the best potential for latency reductions, but it obviously requires a bit of a refactoring effort and might limit the flexibility.