#2642: Improve SpecConstr for join points
-----------------------------------------+----------------------------------
    Reporter:  simonpj                   |       Owner:                  
        Type:  run-time performance bug  |      Status:  new             
    Priority:  normal                    |   Milestone:                  
   Component:  Compiler                  |     Version:  6.8.3           
    Severity:  normal                    |    Keywords:                  
  Difficulty:  Unknown                   |    Testcase:                  
Architecture:  Unknown/Multiple          |          Os:  Unknown/Multiple
-----------------------------------------+----------------------------------
 This is another `SpecConstr` improvement suggestion.  See also #2598,
 #2255.

 Roman writes: Yes, this is turning into a major problem, in fact. This is
 the simplest example I could find:
 {{{
 foo :: Int -> Int :*: (Int :*: Int) -> Int
 foo 0 p = p `seq` 0
 foo i (m :*: (n1 :*: n2)) =
   case (case even (i-n1-n2) of
           True  -> (i `div` 2, (n1-1) :*: (n2-1))
           False -> (i-1, (n1+1) :*: (n2+1))) of
    (j, p) -> foo (if even i then i-m else i+m) ((m-1) :*: p)
 }}}
 The pattern here seems to be
 {{{
         let j x = ....
         in ...(j (C a b)).... (j (C p q)) ...
 }}}
 So a C value is built every time j is called.  Simon Marlow also saw
 something very similar in some STM code he was optimising recently.

 Here's a possible solution:
   * extend `SpecConstr` to work on *non-recursive* functions too.
   * look for the call pattern not in j's RHS (as we do for recursive fns)
 but in the body of the let

 A possible restriction is to do all this only for local, non-recursive fns
 that are themselves defined in the RHS of a recursive function.  The
 motivation would be do it only when we're in a loop, and being in the body
 of a recursive function is a big clue.

 Do you think that would address the cases you've encountered?   It would
 be a bit like making join-points eager to inline (specialisation is a bit
 like partial inlining) except that
   * we'd get sharing when there were two calls applied to the same
 constructor
   * it'd only happen if that argument was taken apart in the join point

 Roman comments: No, the last restriction is not good: we often have
 {{{
 foo p = let j x' = ... foo (x',c) ...
          in ... j (C a) ... j (C b) ...
 }}}
 Here, the join point doesn't take apart the argument. In fact, this
 probably makes it a bit trickier: we want to specialise the join point and
 *then* specialise foo, getting
 {{{
 foo p = let j x' = ... foo (x',c) ...
              {-# RULES j (C z) = j' z #-}
              j' z = ... foo (C z,c) ...
          in ... j' a ... j' b ...

 {-# RULES foo (C x,y) = foo' x y #-}
 foo' x y = ...
 }}}
 A problem is that for `SpecConstr` to specialise `foo`, `j` must be
 specialised
 and *simplified* first to expose the structure of the argument in the
 recursive call. Can this be done without running SpecConstr twice? Or is
 running `pecConstr/Simplify` repeatedly (until it produces no more
 specialisations) acceptable?

-- 
Ticket URL: <http://hackage.haskell.org/trac/ghc/ticket/2642>
GHC <http://www.haskell.org/ghc/>
The Glasgow Haskell Compiler
_______________________________________________
Glasgow-haskell-bugs mailing list
[email protected]
http://www.haskell.org/mailman/listinfo/glasgow-haskell-bugs

Reply via email to