#10626: Missed opportunity for SpecConstr -------------------------------------+------------------------------------- Reporter: simonpj | Owner: Type: bug | Status: new Priority: normal | Milestone: Component: Compiler | Version: 7.10.1 Resolution: | Keywords: Operating System: Unknown/Multiple | Architecture: Type of failure: Runtime | Unknown/Multiple performance bug | Test Case: Blocked By: | Blocking: Related Tickets: | Differential Rev(s): Wiki Page: | -------------------------------------+------------------------------------- Comment (by simonpj): I had a look. There are two things. '''Problem 1'''. With the grotesque (GHC's fault not yours) `SPEC` argument to force specialisation, GHC does not want to generate an infinite number of specialisations; e.g. {{{ f (a:b) = f (a::b) ... }}} This is limited by the (probably un-documented) flag `-fspec-constr- recurisive=N` flag. Its default value is far too low: 3. Set it to 20. The relevant bit in `SpecConstr` is {{{ is_too_recursive :: ScEnv -> (CallPat, ValueEnv) -> Bool -- Count the number of recursive constructors in a call pattern, -- filter out if there are more than the maximum. -- This is only necessary if ForceSpecConstr is in effect: -- otherwise specConstrCount will cause specialisation to terminate. -- See Note [Limit recursive specialisation] -- TODO: make me more accurate }}} And indeed it simply counts data constructors, not even recursive ones. That will seriously limit specialisation in precisely the case where you wanted a lot! At least we could count nesting depth rather than just counting constructors in total. '''Problem 2'''. Your code has {{{ (InterspersesState_Running (snd stored_fs_aguH)) }}} That use of `snd` is fatal, because it's not inlined before `SpecConstr` (since it is applied to an uninformative variable). So `SpecConstr` doesn't "see" the case inside `snd` and that means it generate too few specialisations. If you instead write {{{ -- InterspersesState_YieldedInterspersee stored_fs_aguH InterspersesState_YieldedInterspersee (fs1,fs2) -> gfold_loop sPEC_aguh acc_agui (Flatten_RunningInner -- (InterspersesState_Running (snd stored_fs_aguH)) (InterspersesState_Running fs2) -- (ParamAndState (fst stored_fs_aguH) BeforeYielding)) (ParamAndState fs1 BeforeYielding)) }}} where the old line is commented out, and replaced by the line below, then good things happen, and a single run gives {{{ Rec { -- RHS size: {terms: 33, types: 6, coercions: 0, joins: 0/0} Input.test_$s$wgfold_loop [Occ=LoopBreaker] :: Int# -> Int# -> Int# -> Int# -> Int# [GblId, Arity=4, Caf=NoCafRefs, Str=<S,U><S,U><S,U><S,U>] Input.test_$s$wgfold_loop = \ (sc_s5hq :: Int#) (sc1_s5hr :: Int#) (sc2_s5hs :: Int#) (sc3_s5hp :: Int#) -> case tagToEnum# @ Bool (># sc_s5hq 1000000#) of { False -> Input.test_$s$wgfold_loop (+# sc_s5hq 1#) sc_s5hq sc_s5hq (+# (+# (+# (+# sc3_s5hp sc2_s5hs) 42#) sc1_s5hr) 42#); True -> +# (+# (+# sc3_s5hp sc2_s5hs) 42#) sc1_s5hr } end Rec } }}} -- Ticket URL: <http://ghc.haskell.org/trac/ghc/ticket/10626#comment:5> GHC <http://www.haskell.org/ghc/> The Glasgow Haskell Compiler