Tools/patterns to reduce latency with strategy pattern

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

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])

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:

  1. Use @nospecialize. Can it be applied to parametric types (e.g. something like step1(s::StrategyX{T1, T2, @nospecialize(V)}?
  2. Take the type of applyto off the struct definitions. Will this give the exact same behaviour as @nospecialize (for functions where it is used)?
  3. 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.